Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See the LICENSE and NOTICES files in the project root for more information.

from typing import List
from dataclasses import dataclass
from dataclasses import dataclass, field

from edfi_paging_test.helpers.output_format import OutputFormat
from edfi_paging_test.helpers.log_level import LogLevel
Expand All @@ -26,4 +26,4 @@ class MainArguments:
contentType: OutputFormat
resourceList: List[str]
pageSize: int = 100
log_level: LogLevel = LogLevel.INFO
log_level: LogLevel = LogLevel.INFO
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async def run(args: MainArguments) -> None:
request_client: RequestClient = RequestClient(args)

executor: ThreadPoolExecutor = ThreadPoolExecutor(
max_workers=args.connectionLimit
max_workers=1 # args.connectionLimit TODO AXEL revert
)
loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()

Expand Down
624 changes: 359 additions & 265 deletions src/edfi-paging-test/poetry.lock

Large diffs are not rendered by default.

10 changes: 3 additions & 7 deletions src/edfi-paging-test/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ python = "^3.9"
ConfigArgParse = "^1.5.3"
python-dotenv = "^0.19.2"
requests-oauthlib = "^1.3.1"
requests = "^2.27.1"
urllib3 = "2.2.2"
requests = "^2.32.4"
urllib3 = "^2.5.0"
pandas = "^1.4.1"
errorhandler = "^2.0.1"
numpy = "1.24.3"

[tool.poetry.dev-dependencies]
[tool.poetry.group.dev.dependencies]
flake8 = "^4.0.1"
pytest = "^7.0.1"
mypy = "^0.931"
Expand All @@ -29,10 +29,6 @@ types-requests = "^2.27.12"
data-science-types = "^0.2.23"
pyfakefs = "^4.5.5"
pook = "^1.0.2"



[tool.poetry.group.dev.dependencies]
black = "^24.4.2"

[build-system]
Expand Down
22 changes: 11 additions & 11 deletions src/edfi-performance-test/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/edfi-performance-test/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "Apache-2.0"
[tool.poetry.dependencies]
python = "^3.9"
ConfigArgParse = "^1.5.3"
urllib3 = "2.2.2"
urllib3 = "^2.5.0"
pandas = "^1.4.1"
errorhandler = "^2.0.1"
python-dotenv = "^0.20.0"
Expand All @@ -18,7 +18,7 @@ gevent = "^23.9.1"
jinja2 = "3.1.3"
numpy = "1.24.3"

[tool.poetry.dev-dependencies]
[tool.poetry.group.dev.dependencies]
flake8 = "^4.0.1"
pytest = "^7.0.1"
mypy = "^0.931"
Expand Down
5 changes: 5 additions & 0 deletions src/edfi-query-test/.coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[run]
branch = True
source = edfi_query_test

[report]
4 changes: 4 additions & 0 deletions src/edfi-query-test/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 110
extend-ignore = E203, W503, E501
exclude = .git, __pycache__, .venv
3 changes: 3 additions & 0 deletions src/edfi-query-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.coverage
*.xml
*.json
11 changes: 11 additions & 0 deletions src/edfi-query-test/.mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[mypy]
files=edfi_query_test, tests

[mypy-errorhandler]
ignore_missing_imports = True

[mypy-configargparse]
ignore_missing_imports = True

[mypy-pook]
ignore_missing_imports = True
68 changes: 68 additions & 0 deletions src/edfi-query-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
TODO AXEL
# Query Volume Test

Provides data out performance metrics for the ODS / AP aimed at analysis of
bottlenecks, and providing recommendations for server sizing for representative
agency simulation sizes.

## Getting Started

1. Requires Python 3.9+ and Poetry.
1. Install required Python packages:

```bash
poetry install
```

## Running the Tool

For detailed help, execute `poetry run python edfi_query_test -h`.

Sample call using full integrated security, loading from the sample files
directory:

```bash
poetry run python edfi_query_test -b "https://localhost:54746" -k "testkey" -s "testsecret" -r "resource1" "resource2"
```

### Supported arguments

| Command Line Argument | Required | Description |
| ------------------------------------ | ------------------------------------ | --------------------------------------------------------------------------------------------- |
| `-b` or `--baseUrl` | yes (no default) | ​The base url used to derived api, metadata, oauth, and dependency urls (e.g., http://server) |
| `-k` or `--key` | yes (no default) | The web API OAuth key |
| `-s` or `--secret` | yes (no default) | The web API OAuth secret |
| `-i` or `--ignoreCertificateErrors` | no (default: false) | Ignore certificate errors |
| `-c` or `--connectionLimit` | no (default: 5) | Maximum concurrent connections to api |
| `-o` or `--output` | no (default: out) | Directory for writing results |
| `-t` or `--contentType` | no (default: csv) | Output file content type: CSV, JSON |
| `-r` or `--resourceList` | no (no default) | (Optional) List of resources to test - if not provided, all resources will be retrieved |
| `-p` or `--pageSize` | no (default: 100) | The page size to request. Max: 500. |
| `-l` or `--logLevel` | no (default: INFO) | Override the console output log level: VERBOSE, DEBUG, INFO, WARN, ERROR |
| `-d` or `--description` | no (default: Query Volume Test Run) | Description for the test run |

Each argument can also be set by environment variable, or by using as `.env`
file. See [.env.example](edfi_query_test/.env.example). Arguments provided at
the command line override any arguments provided by environment variable.

### Resource List

When no resource list is provided, the tool will query the OpenAPI (Swagger)
metadata to find all available resources, and then retrieve all of them.

To limit to testing specific resources, simply pass the list of resource names
to be retrieved. For example, `Students StudentSectionAssociations` will return
only those two resources. To retrieve a resource from an extension, prefix that
resource with the extension name and a slash. For example:
`Students tpdm/Candidates` will retrieve all records from the `/ed-fi/Students` and the
`tpdm/Candidates` endpoints. Note that this is not case sensitive. When using a .env file,
the argument is set off as an array, e.g.
`PERF_RESOURCE_LIST=["students", "StudentSchoolAssociations", "tpdm/candidates"]`

### Dev Operations

1. Style check: `poetry run flake8`
2. Static typing check: `poetry run mypy .`
3. Run unit tests: `poetry run pytest`
4. Run unit tests with code coverage: `poetry run coverage run -m pytest tests`
5. View code coverage: `poetry run coverage report`
16 changes: 16 additions & 0 deletions src/edfi-query-test/edfi_query_test/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
PERF_API_BASEURL=https://example.com/WebAPI/api/
PERF_API_KEY=yourClientKey
PERF_API_SECRET=yourClientSecret
PERF_CONNECTION_LIMIT=6
PERF_OUTPUT_DIR=test_output
PERF_CONTENT_TYPE=JSON
PERF_API_PAGE_SIZE=402
PERF_LOG_LEVEL=INFO

# Comment out the following line if you want to test _all_ resources Note that
# resource names are case sensitive and must match the name given in the Ed-Fi
# API's Open API specification (swagger.json).
PERF_RESOURCE_LIST="["students", "studentEducationOrganizationAssociations"]"

# Set to true when using HTTP instead of HTTPS or when using self-signed certificates
IGNORE_TLS_CERTIFICATE=false
4 changes: 4 additions & 0 deletions src/edfi-query-test/edfi_query_test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
# Licensed to the Ed-Fi Alliance under one or more agreements.
# The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
# See the LICENSE and NOTICES files in the project root for more information.
70 changes: 70 additions & 0 deletions src/edfi-query-test/edfi_query_test/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: Apache-2.0
# Licensed to the Ed-Fi Alliance under one or more agreements.
# The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
# See the LICENSE and NOTICES files in the project root for more information.

import asyncio
import sys
import logging

from dotenv import load_dotenv
from errorhandler import ErrorHandler

from edfi_query_test.helpers.argparser import parse_main_arguments
from edfi_query_test.helpers.main_arguments import MainArguments
from edfi_query_test.performance_tester import run
from edfi_query_test.helpers.log_level import LogLevel


logger = logging.getLogger(__name__)


def _redefine_debug_as_verbose(configuration: MainArguments, logger: str) -> None:
requests_logger = logging.getLogger(logger)
if configuration.log_level == LogLevel.VERBOSE:
requests_logger.setLevel(logging.DEBUG)
else:
requests_logger.setLevel(logging.INFO)


def _configure_logging(configuration: MainArguments) -> None:

# Verbose is not a real Python log level, so convert back to DEBUG. Verbose
# is used for quieting other loggers when in DEBUG mode.
log_level = str(LogLevel.DEBUG if configuration.log_level == LogLevel.VERBOSE else configuration.log_level)

logging.basicConfig(
handlers=[
logging.StreamHandler(sys.stdout),
],
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
level=log_level,
)

# These two loggers produce far more than we really want to see in debug
# mode. Therefore we'll redefine _their_ debug modes as "verbose" for our
# purposes.
_redefine_debug_as_verbose(configuration, "requests_oauthlib.oauth2_session")
_redefine_debug_as_verbose(configuration, "urllib3.connectionpool")


async def main() -> None:
load_dotenv()
configuration = parse_main_arguments()
_configure_logging(configuration)

# Important that this comes _after_ the logging configuration
error_tracker = ErrorHandler()

await run(configuration)

if error_tracker.fired:
logger.fatal(
"A fatal error occurred, please review the log output for more information."
)
sys.exit(1)
sys.exit(0)


if __name__ == "__main__":
asyncio.run(main())
4 changes: 4 additions & 0 deletions src/edfi-query-test/edfi_query_test/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
# Licensed to the Ed-Fi Alliance under one or more agreements.
# The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
# See the LICENSE and NOTICES files in the project root for more information.
31 changes: 31 additions & 0 deletions src/edfi-query-test/edfi_query_test/api/api_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: Apache-2.0
# Licensed to the Ed-Fi Alliance under one or more agreements.
# The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
# See the LICENSE and NOTICES files in the project root for more information.


from typing import List, Dict
from dataclasses import dataclass


@dataclass
class APIInfo:
"""
Container for holding details from api version endpoint
"""

version: str
datamodels: List[Dict[str, str]]
urls: Dict[str, str]

@property
def oauth_url(self) -> str:
return self.urls["oauth"]

@property
def api_metadata_url(self) -> str:
return self.urls["openApiMetadata"]

@property
def data_management_api_url(self) -> str:
return self.urls["dataManagementApi"]
Loading
Loading