hackerrank-cli 2026.10.30__tar.gz → 2026.11.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/binaries.yml +1 -1
  2. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/Dockerfile +3 -1
  3. {hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info → hackerrank_cli-2026.11.7}/PKG-INFO +27 -2
  4. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/README.rst +25 -0
  5. hackerrank_cli-2026.11.7/VERSION +1 -0
  6. hackerrank_cli-2026.11.7/docs/source/changelog/2026.11.7.md +7 -0
  7. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/development.rst +1 -1
  8. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/flake.nix +14 -0
  9. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/prek.toml +0 -2
  10. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/pyproject.toml +14 -8
  11. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/spelling_private_dict.txt +6 -0
  12. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli/__init__.py +3 -3
  13. hackerrank_cli-2026.11.7/src/hackerrank_cli/_api.py +205 -0
  14. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli/_version.py +3 -3
  15. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7/src/hackerrank_cli.egg-info}/PKG-INFO +27 -2
  16. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/SOURCES.txt +3 -0
  17. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/requires.txt +1 -1
  18. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/scm_file_list.json +3 -0
  19. hackerrank_cli-2026.11.7/src/hackerrank_cli.egg-info/scm_version.json +8 -0
  20. hackerrank_cli-2026.11.7/tests/test_api.py +240 -0
  21. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help/test_help_root_.txt +2 -1
  22. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/uv.lock +307 -306
  23. hackerrank_cli-2026.10.30/VERSION +0 -1
  24. hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info/scm_version.json +0 -8
  25. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.dockerignore +0 -0
  26. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.gitattributes +0 -0
  27. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/dependabot.yml +0 -0
  28. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/autofix.yml +0 -0
  29. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/ci.yml +0 -0
  30. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/dependabot-merge.yml +0 -0
  31. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/docs.yml +0 -0
  32. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/lint.yml +0 -0
  33. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/nix.yml +0 -0
  34. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/publish-site.yml +0 -0
  35. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.github/workflows/release.yml +0 -0
  36. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.gitignore +0 -0
  37. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/.vale.ini +0 -0
  38. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/LICENSE +0 -0
  39. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/MANIFEST.in +0 -0
  40. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/bin/__init__.py +0 -0
  41. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/bin/hackerrank_wrapper.py +0 -0
  42. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/__init__.py +0 -0
  43. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/__init__.py +0 -0
  44. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/changelog/2026.10.1.1.md +0 -0
  45. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/changelog/2026.10.30.md +0 -0
  46. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/changelog/2026.10.7.md +0 -0
  47. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/changelog/2026.10.8.md +0 -0
  48. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/changelog.md +0 -0
  49. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/cli.rst +0 -0
  50. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/conf.py +0 -0
  51. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/index.rst +0 -0
  52. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/macos-releases.rst +0 -0
  53. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/migration.rst +0 -0
  54. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/docs/source/usage.rst +0 -0
  55. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/flake.lock +0 -0
  56. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/newsfragments/.gitkeep +0 -0
  57. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/setup.cfg +0 -0
  58. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli/__main__.py +0 -0
  59. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli/_sources.py +0 -0
  60. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli/py.typed +0 -0
  61. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/dependency_links.txt +0 -0
  62. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/entry_points.txt +0 -0
  63. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/not-zip-safe +0 -0
  64. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/src/hackerrank_cli.egg-info/top_level.txt +0 -0
  65. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/styles/snapper/LongProseLine.yml +0 -0
  66. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/styles/snapper/SemanticLineBreaks.yml +0 -0
  67. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/__init__.py +0 -0
  68. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_archive.py +0 -0
  69. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_cli.py +0 -0
  70. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help/test_help_questions_.txt +0 -0
  71. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help/test_help_questions_archive_.txt +0 -0
  72. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help/test_help_questions_set_system_prompt_.txt +0 -0
  73. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help/test_help_questions_upload_.txt +0 -0
  74. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_help.py +0 -0
  75. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_http.py +0 -0
  76. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_sources.py +0 -0
  77. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/tests/test_system_prompt.py +0 -0
  78. {hackerrank_cli-2026.10.30 → hackerrank_cli-2026.11.7}/zizmor.yml +0 -0
@@ -67,7 +67,7 @@ jobs:
67
67
  - name: Import the Developer ID certificate
68
68
  id: signing_certificate
69
69
  if: runner.os == 'macOS' && inputs.notarize
70
- uses: Apple-Actions/import-codesign-certs@77ba2a2a8f5cfc8c405ebf3159f767c323e238f9 # v7.1.0
70
+ uses: Apple-Actions/import-codesign-certs@288b8ec97b82c66b6f2a6baaf7acc8dfbcbe331c # v7.2.0
71
71
  with:
72
72
  p12-file-base64: ${{ secrets.DEVELOPER_ID_APP_CERT_P12_BASE64 }}
73
73
  p12-password: ${{ secrets.DEVELOPER_ID_APP_CERT_PASSWORD }}
@@ -4,12 +4,14 @@ COPY --from=ghcr.io/astral-sh/uv:0.13.0 /uv /bin/uv
4
4
  WORKDIR /build
5
5
  COPY . .
6
6
  ARG HACKERRANK_CLI_VERSION=0.0.0
7
+ RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
7
8
  RUN SETUPTOOLS_SCM_PRETEND_VERSION_FOR_HACKERRANK_CLI=$HACKERRANK_CLI_VERSION uv build --wheel --out-dir /wheels
9
+ RUN uv pip compile pyproject.toml -o /tmp/runtime.txt && uv run --no-project --with pip python -m pip wheel --wheel-dir /wheels -r /tmp/runtime.txt
8
10
 
9
11
  FROM python:3.13-slim
10
12
  LABEL org.opencontainers.image.source="https://github.com/adamtheturtle/hackerrank-cli"
11
13
  LABEL org.opencontainers.image.description="Upload HackerRank question starter code"
12
14
  LABEL org.opencontainers.image.licenses="MIT"
13
15
  COPY --from=build /wheels /wheels
14
- RUN pip install --no-cache-dir /wheels/*.whl && rm -rf /wheels
16
+ RUN pip install --no-cache-dir --no-index --no-deps /wheels/*.whl && rm -rf /wheels
15
17
  ENTRYPOINT ["hackerrank"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hackerrank-cli
3
- Version: 2026.10.30
3
+ Version: 2026.11.7
4
4
  Summary: Upload starter files to existing HackerRank project questions.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -19,7 +19,7 @@ Requires-Python: >=3.13
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
21
  Requires-Dist: click<8.6,>=8.3
22
- Requires-Dist: hackerrank>=2026.10.30
22
+ Requires-Dist: hackerrank>=2026.11.7
23
23
  Requires-Dist: httpx>=0.28
24
24
  Requires-Dist: httpx2>=2.12
25
25
  Requires-Dist: pathspec>=1
@@ -184,3 +184,28 @@ Prompts may contain up to 15,000 characters.
184
184
  Text is sent exactly as read, including whitespace and line endings.
185
185
  An empty file explicitly clears the prompt.
186
186
  The command reads the question first to preserve its other metadata.
187
+
188
+ Complete API access
189
+ -------------------
190
+
191
+ The ``api`` command group exposes all 76 documented v3, ATS and SCIM
192
+ operations.
193
+ Use ``hackerrank api operations`` to list names and ``api describe OPERATION``
194
+ to inspect parameters and body schemas.
195
+
196
+ .. code-block:: console
197
+
198
+ hackerrank api call api_list_all_tests --query '{"limit": 10}'
199
+ hackerrank api call api_update_interviewer_note --path '{"interview_id": "123"}' --body-file note.json --dry-run
200
+
201
+ ``--path`` and ``--query`` accept JSON objects.
202
+ ``--body-file`` reads a JSON object from a UTF-8 file, or stdin when set to
203
+ ``-``.
204
+ Real v3 requests use ``HACKERRANK_API_TOKEN``.
205
+ SCIM requests use their own ``HACKERRANK_SCIM_API_TOKEN`` and provisioning
206
+ host.
207
+ Dry runs validate requests before reading credentials and make no API calls.
208
+ Responses include status, headers and complete JSON data, including empty and
209
+ pending responses.
210
+
211
+ The complete API commands require ``hackerrank>=2026.11.7``.
@@ -157,3 +157,28 @@ Prompts may contain up to 15,000 characters.
157
157
  Text is sent exactly as read, including whitespace and line endings.
158
158
  An empty file explicitly clears the prompt.
159
159
  The command reads the question first to preserve its other metadata.
160
+
161
+ Complete API access
162
+ -------------------
163
+
164
+ The ``api`` command group exposes all 76 documented v3, ATS and SCIM
165
+ operations.
166
+ Use ``hackerrank api operations`` to list names and ``api describe OPERATION``
167
+ to inspect parameters and body schemas.
168
+
169
+ .. code-block:: console
170
+
171
+ hackerrank api call api_list_all_tests --query '{"limit": 10}'
172
+ hackerrank api call api_update_interviewer_note --path '{"interview_id": "123"}' --body-file note.json --dry-run
173
+
174
+ ``--path`` and ``--query`` accept JSON objects.
175
+ ``--body-file`` reads a JSON object from a UTF-8 file, or stdin when set to
176
+ ``-``.
177
+ Real v3 requests use ``HACKERRANK_API_TOKEN``.
178
+ SCIM requests use their own ``HACKERRANK_SCIM_API_TOKEN`` and provisioning
179
+ host.
180
+ Dry runs validate requests before reading credentials and make no API calls.
181
+ Responses include status, headers and complete JSON data, including empty and
182
+ pending responses.
183
+
184
+ The complete API commands require ``hackerrank>=2026.11.7``.
@@ -0,0 +1 @@
1
+ 2026.11.7
@@ -0,0 +1,7 @@
1
+ # 2026.11.7 (2026-11-07)
2
+
3
+ ## Features
4
+
5
+ - Expose all documented HackerRank v3, ATS and SCIM operations through the API
6
+ command group, with offline discovery and validation.
7
+ ([#69](https://github.com/adamtheturtle/hackerrank-cli/issues/69))
@@ -6,7 +6,7 @@ This standalone repository follows ``coderpad-cli`` main at
6
6
  It adapts the src layout, Click entry points, setuptools-scm versions, uv
7
7
  lockfile, strict checks, 100% branch coverage, Sphinx, Towncrier, and
8
8
  distribution workflows.
9
- The SDK baseline is the released ``hackerrank==2026.10.30`` in ``uv.lock``.
9
+ The SDK baseline is the released ``hackerrank==2026.11.7`` in ``uv.lock``.
10
10
  Python 3.13 or later is required by the SDK.
11
11
 
12
12
  The released SDK exposes ``questions.upload_project_zip``.
@@ -52,6 +52,19 @@
52
52
  else
53
53
  "0.0.0+${self.shortRev or self.dirtyShortRev or "unknown"}";
54
54
 
55
+ # The pinned SDK is built from source, so supply its build backend.
56
+ hackerrankSourceOverlay = final: prev: {
57
+ hackerrank = prev.hackerrank.overrideAttrs (old: {
58
+ nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ final.resolveBuildSystem {
59
+ setuptools = [ ];
60
+ setuptools-scm = [ ];
61
+ };
62
+ env = (old.env or { }) // {
63
+ SETUPTOOLS_SCM_PRETEND_VERSION = old.version;
64
+ };
65
+ });
66
+ };
67
+
55
68
  # Override hackerrank-cli to set the version
56
69
  hackerrankCliOverlay = final: prev: {
57
70
  hackerrank-cli = prev.hackerrank-cli.overrideAttrs (old: {
@@ -74,6 +87,7 @@
74
87
  lib.composeManyExtensions [
75
88
  pyproject-build-systems.overlays.wheel
76
89
  overlay
90
+ hackerrankSourceOverlay
77
91
  hackerrankCliOverlay
78
92
  ]
79
93
  )
@@ -506,8 +506,6 @@ hooks = [
506
506
  {
507
507
  id = "vale-sync",
508
508
  name = "vale sync",
509
- # The Python Vale wrapper may fail transiently while downloading its binary; see
510
- # https://github.com/daniperez/vale-python-package/issues/77
511
509
  entry = "uv run --locked --group=dev vale sync",
512
510
  language = "system",
513
511
  pass_filenames = false,
@@ -21,7 +21,13 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.15",
22
22
  ]
23
23
  dynamic = [ "version" ]
24
- dependencies = [ "click>=8.3,<8.6", "hackerrank>=2026.10.30", "httpx>=0.28", "httpx2>=2.12", "pathspec>=1" ]
24
+ dependencies = [
25
+ "click>=8.3,<8.6",
26
+ "hackerrank>=2026.11.7",
27
+ "httpx>=0.28",
28
+ "httpx2>=2.12",
29
+ "pathspec>=1",
30
+ ]
25
31
  urls.Documentation = "https://adamtheturtle.github.io/hackerrank-cli/"
26
32
  urls.Issues = "https://github.com/adamtheturtle/hackerrank-cli/issues"
27
33
  urls.Source = "https://github.com/adamtheturtle/hackerrank-cli"
@@ -38,12 +44,12 @@ dev = [
38
44
  "doccmd==2026.9.16.1",
39
45
  "furo==2025.12.19",
40
46
  "interrogate==1.7.0",
41
- "mypy==2.4.0",
47
+ "mypy==2.4.1",
42
48
  "mypy-pytest-parametrize==2026.10.5.1",
43
49
  "mypy-strict-kwargs==2026.10.19",
44
50
  "myst-parser==5.1.0",
45
51
  "no-defaults==2026.9.1",
46
- "prek==0.5.7",
52
+ "prek==0.5.9",
47
53
  "pydocstringformatter==1.0.0",
48
54
  "pylint[spelling]==4.1.3",
49
55
  "pylint-per-file-ignores==4.0.0",
@@ -51,13 +57,13 @@ dev = [
51
57
  "pyrefly==1.4.0",
52
58
  "pyright==1.1.414",
53
59
  "pyroma==5.0.1",
54
- "pytest==9.1.1",
60
+ "pytest==9.2.0",
55
61
  "pytest-beartype==0.3.0",
56
62
  "pytest-cov==7.1",
57
63
  "pytest-regressions==2.11.0",
58
64
  "pytest-xdist==3.8.0",
59
65
  "respx==0.23.1",
60
- "ruff==0.17.2",
66
+ "ruff==0.17.3",
61
67
  "setuptools>=77",
62
68
  "setuptools-scm>=8.1",
63
69
  "shellcheck-py==0.11.0.1",
@@ -70,11 +76,11 @@ dev = [
70
76
  "strict-kwargs==2026.10.9",
71
77
  "towncrier==26.9",
72
78
  "twine==7.0.0",
73
- "ty==0.0.92",
74
- "vale==3.25.1.0",
79
+ "ty==0.0.96",
80
+ "vale==3.26.0.1",
75
81
  "vulture==2.16",
76
82
  "yamlfix==1.19.1",
77
- "zizmor==1.31.1",
83
+ "zizmor==1.32.0",
78
84
  ]
79
85
  binary = [ "pyinstaller>=6.12" ]
80
86
 
@@ -23,22 +23,26 @@ Symlinked
23
23
  Towncrier
24
24
  UTF
25
25
  Untagged
26
+ api
26
27
  autofix
27
28
  backend
28
29
  backoff
29
30
  basename
30
31
  bugfix
32
+ catalogue
31
33
  changelog
32
34
  cli
33
35
  dereference
34
36
  dereferenced
35
37
  dereferences
36
38
  dereferencing
39
+ discoverable
37
40
  docstrings
38
41
  filesystem
39
42
  frontend
40
43
  fullstack
41
44
  hackerrank
45
+ injectable
42
46
  jsonschema
43
47
  jsonschema's
44
48
  keychain
@@ -50,9 +54,11 @@ nonretryable
50
54
  prebuilt
51
55
  prek
52
56
  retryable
57
+ schemas
53
58
  scm
54
59
  setuptools
55
60
  src
61
+ stdin
56
62
  symlink
57
63
  symlinks
58
64
  uploader
@@ -15,6 +15,7 @@ from hackerrank.client import HackerRank
15
15
  from hackerrank.exceptions import HackerRankError
16
16
  from hackerrank.transports import Transport
17
17
 
18
+ from hackerrank_cli._api import create_api_cli
18
19
  from hackerrank_cli._sources import PreparedSource, prepare_source
19
20
 
20
21
 
@@ -27,9 +28,7 @@ def create_cli(*, transport: Transport | None = None) -> click.Group:
27
28
  prog_name="hackerrank",
28
29
  )
29
30
  def cli() -> None:
30
- """Upload starter files to existing HackerRank project
31
- questions.
32
- """
31
+ """Manage HackerRank uploads, prompts and the complete API."""
33
32
 
34
33
  @cli.group()
35
34
  def questions() -> None:
@@ -162,6 +161,7 @@ def create_cli(*, transport: Transport | None = None) -> click.Group:
162
161
  _ = questions.command()(set_system_prompt)
163
162
  _ = questions.command()(archive)
164
163
  _ = questions.command()(upload)
164
+ cli.add_command(cmd=create_api_cli(transport=transport))
165
165
  return cli
166
166
 
167
167
 
@@ -0,0 +1,205 @@
1
+ """Expose every documented API operation through the SDK catalogue."""
2
+
3
+ import json
4
+ import os
5
+ from dataclasses import asdict, dataclass
6
+ from typing import TextIO
7
+
8
+ import click
9
+ import httpx
10
+ import httpx2
11
+ from hackerrank.api import (
12
+ api_catalogue,
13
+ api_operations,
14
+ parse_api_object,
15
+ prepare_operation,
16
+ )
17
+ from hackerrank.client import HackerRank
18
+ from hackerrank.exceptions import HackerRankError
19
+ from hackerrank.transports import Transport
20
+ from hackerrank.types import JSONValue
21
+
22
+
23
+ @dataclass(frozen=True, kw_only=True)
24
+ class APICommandRequest:
25
+ """The request inputs supplied to the API command."""
26
+
27
+ operation: str
28
+ path_json: str
29
+ query_json: str
30
+ body_file: TextIO | None
31
+
32
+
33
+ def create_api_cli(*, transport: Transport | None) -> click.Group:
34
+ """Create catalogue, schema and execution commands with an injectable
35
+ transport.
36
+ """
37
+
38
+ @click.group(name="api")
39
+ def api() -> None:
40
+ """Call every documented v3, ATS and SCIM operation."""
41
+
42
+ def operations() -> None:
43
+ """List operation names, methods, paths and summaries without
44
+ credentials.
45
+ """
46
+ rows = [
47
+ {
48
+ "operation": value.id,
49
+ "method": value.method,
50
+ "path": value.path,
51
+ "summary": value.summary,
52
+ "group": value.group,
53
+ }
54
+ for value in api_operations().values()
55
+ ]
56
+ click.echo(message=json.dumps(obj=rows, indent=2))
57
+
58
+ @click.argument("operation")
59
+ def describe(operation: str) -> None:
60
+ """Show an operation's parameters, body schema and referenced
61
+ definitions.
62
+ """
63
+ selected = api_operations().get(operation)
64
+ if selected is None:
65
+ raise click.BadParameter(
66
+ message="Unknown API operation.", param_hint="OPERATION"
67
+ )
68
+ click.echo(
69
+ message=json.dumps(
70
+ obj={
71
+ "operation": asdict(obj=selected),
72
+ "definitions": api_catalogue()["definitions"],
73
+ },
74
+ indent=2,
75
+ )
76
+ )
77
+
78
+ @click.argument("operation")
79
+ @click.option(
80
+ "--path",
81
+ "path_json",
82
+ default="{}",
83
+ help="JSON object of path parameters.",
84
+ )
85
+ @click.option(
86
+ "--query",
87
+ "query_json",
88
+ default="{}",
89
+ help="JSON object of query fields; arrays use CSV encoding.",
90
+ )
91
+ @click.option(
92
+ "--body-file",
93
+ type=click.File(mode="r", encoding="utf-8"),
94
+ help="JSON request body file, or - for stdin.",
95
+ )
96
+ @click.option(
97
+ "--dry-run",
98
+ is_flag=True,
99
+ help="Validate without credentials or network access.",
100
+ )
101
+ def call(
102
+ operation: str,
103
+ path_json: str,
104
+ query_json: str,
105
+ body_file: TextIO | None,
106
+ *,
107
+ dry_run: bool,
108
+ ) -> None:
109
+ """Execute OPERATION and print status, headers and complete JSON
110
+ data.
111
+ """
112
+ _call_api(
113
+ request=APICommandRequest(
114
+ operation=operation,
115
+ path_json=path_json,
116
+ query_json=query_json,
117
+ body_file=body_file,
118
+ ),
119
+ dry_run=dry_run,
120
+ transport=transport,
121
+ )
122
+
123
+ _ = api.command(name="operations")(operations)
124
+ _ = api.command(name="describe")(describe)
125
+ _ = api.command(name="call")(call)
126
+ return api
127
+
128
+
129
+ def _path_parameters(*, values: dict[str, JSONValue]) -> dict[str, str | int]:
130
+ """Reject structured values before the SDK encodes path segments."""
131
+ result: dict[str, str | int] = {}
132
+ for name, value in values.items():
133
+ if isinstance(value, bool) or not isinstance(value, str | int):
134
+ message = "Path parameters must be strings or integers."
135
+ raise TypeError(message)
136
+ result[name] = value
137
+ return result
138
+
139
+
140
+ def _call_api(
141
+ *,
142
+ request: APICommandRequest,
143
+ dry_run: bool,
144
+ transport: Transport | None,
145
+ ) -> None:
146
+ """Prepare a request before credential access, then execute it through
147
+ the SDK.
148
+ """
149
+ operation = request.operation
150
+ path_json = request.path_json
151
+ query_json = request.query_json
152
+ body_file = request.body_file
153
+ try:
154
+ path_values = parse_api_object(text=path_json)
155
+ path_parameters = _path_parameters(values=path_values)
156
+ query = parse_api_object(text=query_json)
157
+ body = (
158
+ None
159
+ if body_file is None
160
+ else parse_api_object(text=body_file.read())
161
+ )
162
+ selected, path, _ = prepare_operation(
163
+ operation=operation,
164
+ path_parameters=path_parameters,
165
+ query=query,
166
+ body=body,
167
+ )
168
+ except (ValueError, TypeError, OSError) as error:
169
+ raise click.BadParameter(
170
+ message=f"Invalid API input: {error}"
171
+ ) from None
172
+ if dry_run:
173
+ click.echo(message=f"Would call {selected.method} {path}")
174
+ return
175
+ variable = (
176
+ "HACKERRANK_SCIM_API_TOKEN"
177
+ if selected.scim
178
+ else "HACKERRANK_API_TOKEN"
179
+ )
180
+ credential = os.environ.get(key=variable, default="")
181
+ if not bool(credential.strip()):
182
+ raise click.ClickException(
183
+ message=f"Set {variable} to a nonblank API token."
184
+ )
185
+ try:
186
+ with HackerRank(
187
+ api_key="scim-only" if selected.scim else credential,
188
+ transport=transport,
189
+ ) as client:
190
+ response = client.api.call(
191
+ operation=operation,
192
+ path_parameters=path_parameters,
193
+ query=query,
194
+ body=body,
195
+ scim_api_key=credential if selected.scim else None,
196
+ )
197
+ except HackerRankError as error:
198
+ raise click.ClickException(
199
+ message=f"HackerRank request failed (HTTP {error.status_code})."
200
+ ) from None
201
+ except (httpx.TransportError, httpx2.TransportError):
202
+ raise click.ClickException(
203
+ message="Could not reach HackerRank."
204
+ ) from None
205
+ click.echo(message=json.dumps(obj=asdict(obj=response), indent=2))
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2026.10.30'
22
- __version_tuple__ = version_tuple = (2026, 10, 30)
21
+ __version__ = version = '2026.11.7'
22
+ __version_tuple__ = version_tuple = (2026, 11, 7)
23
23
 
24
- __commit_id__ = commit_id = 'gf7fd0dcbf'
24
+ __commit_id__ = commit_id = 'gaed479392'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hackerrank-cli
3
- Version: 2026.10.30
3
+ Version: 2026.11.7
4
4
  Summary: Upload starter files to existing HackerRank project questions.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -19,7 +19,7 @@ Requires-Python: >=3.13
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
21
  Requires-Dist: click<8.6,>=8.3
22
- Requires-Dist: hackerrank>=2026.10.30
22
+ Requires-Dist: hackerrank>=2026.11.7
23
23
  Requires-Dist: httpx>=0.28
24
24
  Requires-Dist: httpx2>=2.12
25
25
  Requires-Dist: pathspec>=1
@@ -184,3 +184,28 @@ Prompts may contain up to 15,000 characters.
184
184
  Text is sent exactly as read, including whitespace and line endings.
185
185
  An empty file explicitly clears the prompt.
186
186
  The command reads the question first to preserve its other metadata.
187
+
188
+ Complete API access
189
+ -------------------
190
+
191
+ The ``api`` command group exposes all 76 documented v3, ATS and SCIM
192
+ operations.
193
+ Use ``hackerrank api operations`` to list names and ``api describe OPERATION``
194
+ to inspect parameters and body schemas.
195
+
196
+ .. code-block:: console
197
+
198
+ hackerrank api call api_list_all_tests --query '{"limit": 10}'
199
+ hackerrank api call api_update_interviewer_note --path '{"interview_id": "123"}' --body-file note.json --dry-run
200
+
201
+ ``--path`` and ``--query`` accept JSON objects.
202
+ ``--body-file`` reads a JSON object from a UTF-8 file, or stdin when set to
203
+ ``-``.
204
+ Real v3 requests use ``HACKERRANK_API_TOKEN``.
205
+ SCIM requests use their own ``HACKERRANK_SCIM_API_TOKEN`` and provisioning
206
+ host.
207
+ Dry runs validate requests before reading credentials and make no API calls.
208
+ Responses include status, headers and complete JSON data, including empty and
209
+ pending responses.
210
+
211
+ The complete API commands require ``hackerrank>=2026.11.7``.
@@ -40,9 +40,11 @@ docs/source/changelog/2026.10.1.1.md
40
40
  docs/source/changelog/2026.10.30.md
41
41
  docs/source/changelog/2026.10.7.md
42
42
  docs/source/changelog/2026.10.8.md
43
+ docs/source/changelog/2026.11.7.md
43
44
  newsfragments/.gitkeep
44
45
  src/hackerrank_cli/__init__.py
45
46
  src/hackerrank_cli/__main__.py
47
+ src/hackerrank_cli/_api.py
46
48
  src/hackerrank_cli/_sources.py
47
49
  src/hackerrank_cli/_version.py
48
50
  src/hackerrank_cli/py.typed
@@ -58,6 +60,7 @@ src/hackerrank_cli.egg-info/top_level.txt
58
60
  styles/snapper/LongProseLine.yml
59
61
  styles/snapper/SemanticLineBreaks.yml
60
62
  tests/__init__.py
63
+ tests/test_api.py
61
64
  tests/test_archive.py
62
65
  tests/test_cli.py
63
66
  tests/test_help.py
@@ -1,5 +1,5 @@
1
1
  click<8.6,>=8.3
2
- hackerrank>=2026.10.30
2
+ hackerrank>=2026.11.7
3
3
  httpx>=0.28
4
4
  httpx2>=2.12
5
5
  pathspec>=1
@@ -28,6 +28,7 @@
28
28
  "docs/source/changelog/2026.10.30.md",
29
29
  "docs/source/changelog/2026.10.7.md",
30
30
  "docs/source/changelog/2026.10.8.md",
31
+ "docs/source/changelog/2026.11.7.md",
31
32
  "docs/source/cli.rst",
32
33
  "docs/source/conf.py",
33
34
  "docs/source/development.rst",
@@ -43,11 +44,13 @@
43
44
  "spelling_private_dict.txt",
44
45
  "src/hackerrank_cli/__init__.py",
45
46
  "src/hackerrank_cli/__main__.py",
47
+ "src/hackerrank_cli/_api.py",
46
48
  "src/hackerrank_cli/_sources.py",
47
49
  "src/hackerrank_cli/py.typed",
48
50
  "styles/snapper/LongProseLine.yml",
49
51
  "styles/snapper/SemanticLineBreaks.yml",
50
52
  "tests/__init__.py",
53
+ "tests/test_api.py",
51
54
  "tests/test_archive.py",
52
55
  "tests/test_cli.py",
53
56
  "tests/test_help.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2026.11.7",
3
+ "distance": 0,
4
+ "node": "gaed479392ff0082e9c01ce4abba61fb0da024195",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-11-07"
8
+ }