-
Notifications
You must be signed in to change notification settings - Fork 10
WIP: Add initial API documentation and tests #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
hakbailey
wants to merge
3
commits into
ansible:main
Choose a base branch
from
hakbailey:add-api-spec
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,13 @@ Hi there! We're excited to have you as a contributor. | |
- [pattern-service](#pattern-service) | ||
- [Table of contents](#table-of-contents) | ||
- [Things to know prior to submitting code](#things-to-know-prior-to-submitting-code) | ||
- [Build and Run the Development Environment](#build-and-run-the-development-environment) | ||
- [Clone the repo](#clone-the-repo) | ||
- [Configure python environment](#configure-python-environment) | ||
- [Configure and run the application](#configure-and-run-the-application) | ||
- [Build and Run the Development Environment](#build-and-run-the-development-environment) | ||
- [Clone the repo](#clone-the-repo) | ||
- [Configure python environment](#configure-python-environment) | ||
- [Configure and run the application](#configure-and-run-the-application) | ||
- [Testing](#testing) | ||
- [Running tests locally](#running-tests-locally) | ||
- [Building API Documentation](#building-api-documentation) | ||
|
||
## Things to know prior to submitting code | ||
|
||
|
@@ -19,15 +22,15 @@ Hi there! We're excited to have you as a contributor. | |
- If collaborating with someone else on the same branch, consider using `--force-with-lease` instead of `--force`. This will prevent you from accidentally overwriting commits pushed by someone else. For more information, see [git push docs](https://git-scm.com/docs/git-push#git-push---force-with-leaseltrefnamegt). | ||
- We ask all of our community members and contributors to adhere to the [Ansible code of conduct](http://docs.ansible.com/ansible/latest/community/code_of_conduct.html). If you have questions, or need assistance, please reach out to our community team at [[email protected]](mailto:[email protected]) | ||
|
||
### Build and Run the Development Environment | ||
## Build and Run the Development Environment | ||
|
||
#### Clone the repo | ||
### Clone the repo | ||
|
||
If you have not already done so, you will need to clone, or create a local copy, of the [pattern-service repository](https://github.com/ansible/pattern-service). | ||
For more on how to clone the repo, view [git clone help](https://git-scm.com/docs/git-clone). | ||
Once you have a local copy, run the commands in the following sections from the root of the project tree. | ||
|
||
#### Configure python environment | ||
### Configure python environment | ||
hakbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Create python virtual environment using one of the below commands: | ||
|
||
|
@@ -41,8 +44,22 @@ Install required python modules | |
|
||
`pip install -r ./requirements-all.txt` | ||
|
||
#### Configure and run the application | ||
### Configure and run the application | ||
|
||
`python manage.py migrate && python manage.py runserver` | ||
|
||
The application can be reached in your browser at `https://localhost:8000/` | ||
The application can be reached in your browser at `http://localhost:8000/` | ||
|
||
## Testing | ||
|
||
All code contributions should include unit tests. Functional and integration tests may also be required for some types of code changes. | ||
|
||
### Running tests locally | ||
|
||
This repository uses pytest to run its test suite. To run the tests locally, run `make test`. | ||
|
||
## Building API Documentation | ||
|
||
The pattern service includes support for generating an OpenAPI Description of the API. To build the documentation locally, run `make generate-api-spec`. | ||
|
||
HTML-rendered API documentation can also be accessed within the running application at `http://localhost:8000/api/pattern-service/v1/docs/` or `http://localhost:8000/api/pattern-service/v1/docs/redoc/` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import pytest | ||
from freezegun import freeze_time | ||
from rest_framework import status | ||
from rest_framework.test import APIClient | ||
|
||
from core import models | ||
from docs import examples | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def frozen_time(): | ||
with freeze_time("2025-06-25 01:02:03"): | ||
yield | ||
|
||
|
||
@pytest.fixture() | ||
def client(): | ||
client = APIClient() | ||
return client | ||
|
||
|
||
@pytest.fixture | ||
def automation(db, pattern_instance) -> models.Automation: | ||
automation = models.Automation.objects.create( | ||
automation_type=examples.automation_response.value["automation_type"], | ||
automation_id=examples.automation_response.value["automation_id"], | ||
pattern_instance=pattern_instance, | ||
primary=examples.automation_response.value["primary"], | ||
) | ||
return automation | ||
|
||
|
||
@pytest.fixture | ||
def controller_label(db) -> models.ControllerLabel: | ||
controller_label = models.ControllerLabel.objects.create(label_id=examples.controller_label_response.value["label_id"]) | ||
return controller_label | ||
|
||
|
||
@pytest.fixture | ||
def pattern(db) -> models.Pattern: | ||
pattern = models.Pattern.objects.create( | ||
collection_name=examples.pattern_post.value["collection_name"], | ||
collection_version=examples.pattern_post.value["collection_version"], | ||
pattern_name=examples.pattern_post.value["pattern_name"], | ||
) | ||
return pattern | ||
|
||
|
||
@pytest.fixture | ||
def pattern_instance(db, pattern) -> models.PatternInstance: | ||
pattern_instance = models.PatternInstance.objects.create( | ||
credentials=examples.pattern_instance_post.value["credentials"], | ||
executors=examples.pattern_instance_post.value["executors"], | ||
organization_id=1, | ||
pattern=pattern, | ||
) | ||
return pattern_instance | ||
|
||
|
||
def test_retrieve_automation_success(db, client, automation): | ||
url = f"/api/pattern-service/v1/automations/{automation.pk}/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == examples.automation_response.value | ||
|
||
|
||
def test_list_automations_success(db, client, automation): | ||
url = "/api/pattern-service/v1/automations/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == [examples.automation_response.value] | ||
|
||
|
||
def test_retrieve_controller_label_success(db, client, controller_label): | ||
url = f"/api/pattern-service/v1/controller_labels/{controller_label.pk}/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == examples.controller_label_response.value | ||
|
||
|
||
def test_list_controller_labels_success(db, client, controller_label): | ||
url = "/api/pattern-service/v1/controller_labels/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == [examples.controller_label_response.value] | ||
|
||
|
||
def test_create_pattern_success(db, client): | ||
url = "/api/pattern-service/v1/patterns/" | ||
data = examples.pattern_post.value | ||
response = client.post(url, data, format="json") | ||
assert response.status_code == status.HTTP_201_CREATED | ||
assert response.json() == examples.pattern_response.value | ||
|
||
|
||
def test_retrieve_pattern_success(db, client, pattern): | ||
url = f"/api/pattern-service/v1/patterns/{pattern.pk}/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == examples.pattern_response.value | ||
|
||
|
||
def test_list_patterns_success(db, client, pattern): | ||
url = "/api/pattern-service/v1/patterns/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == [examples.pattern_response.value] | ||
|
||
|
||
def test_create_pattern_instance_success(db, client, pattern): | ||
url = "/api/pattern-service/v1/pattern_instances/" | ||
data = examples.pattern_instance_post.value | ||
data["pattern"] = pattern.pk | ||
response = client.post(url, data, format="json") | ||
assert response.status_code == status.HTTP_201_CREATED | ||
assert response.json() == examples.pattern_instance_response.value | ||
|
||
|
||
def test_retrieve_pattern_instance_success(db, client, pattern_instance): | ||
url = f"/api/pattern-service/v1/pattern_instances/{pattern_instance.pk}/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == examples.pattern_instance_response.value | ||
|
||
|
||
def test_list_pattern_instances_success(db, client, pattern_instance): | ||
url = "/api/pattern-service/v1/pattern_instances/" | ||
response = client.get(url) | ||
assert response.status_code == status.HTTP_200_OK | ||
assert response.json() == [examples.pattern_instance_response.value] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,105 @@ | ||
from ansible_base.lib.utils.views.ansible_base import AnsibleBaseView | ||
from drf_spectacular.utils import extend_schema | ||
from drf_spectacular.utils import extend_schema_view | ||
from rest_framework.decorators import api_view | ||
from rest_framework.request import Request | ||
from rest_framework.response import Response | ||
from rest_framework.viewsets import ModelViewSet | ||
|
||
from .models import Automation | ||
from .models import ControllerLabel | ||
from .models import Pattern | ||
from .models import PatternInstance | ||
from .serializers import AutomationSerializer | ||
from .serializers import ControllerLabelSerializer | ||
from .serializers import PatternInstanceSerializer | ||
from .serializers import PatternSerializer | ||
from core.models import Automation | ||
from core.models import ControllerLabel | ||
from core.models import Pattern | ||
from core.models import PatternInstance | ||
from core.serializers import AutomationSerializer | ||
from core.serializers import ControllerLabelSerializer | ||
from core.serializers import PatternInstanceSerializer | ||
from core.serializers import PatternSerializer | ||
from docs import examples | ||
|
||
|
||
class CoreViewSet(AnsibleBaseView): | ||
pass | ||
|
||
|
||
@extend_schema_view( | ||
create=extend_schema( | ||
description="Add an Ansible pattern to the service.", | ||
examples=[examples.pattern_post, examples.pattern_response], | ||
), | ||
list=extend_schema( | ||
description="Retrieve information about all Ansible pattern added to the service.", | ||
examples=[examples.pattern_response], | ||
), | ||
retrieve=extend_schema( | ||
description="Retrieve information about a single Ansible pattern by ID.", | ||
examples=[examples.pattern_response], | ||
), | ||
) | ||
class PatternViewSet(CoreViewSet, ModelViewSet): | ||
http_method_names = ["get", "post", "delete", "head", "options"] | ||
queryset = Pattern.objects.all() | ||
serializer_class = PatternSerializer | ||
|
||
|
||
@extend_schema_view( | ||
list=extend_schema( | ||
description="Retrieve information about all controller labels created by the service.", | ||
examples=[examples.automation_response], | ||
), | ||
retrieve=extend_schema( | ||
description="Retrieve information about a single controller label by ID.", | ||
examples=[examples.automation_response], | ||
), | ||
) | ||
class ControllerLabelViewSet(CoreViewSet, ModelViewSet): | ||
http_method_names = ["get", "delete", "head", "options"] | ||
queryset = ControllerLabel.objects.all() | ||
serializer_class = ControllerLabelSerializer | ||
|
||
|
||
@extend_schema_view( | ||
create=extend_schema( | ||
description="Create an instance of an Ansible pattern, creating its defined AAP resources and saving their IDs.", | ||
examples=[examples.pattern_instance_post, examples.pattern_instance_response], | ||
), | ||
list=extend_schema( | ||
description="Retrieve information about all pattern instances.", | ||
examples=[examples.pattern_instance_response], | ||
), | ||
retrieve=extend_schema( | ||
description="Retrieve information about a single pattern instance by ID.", | ||
examples=[examples.pattern_instance_response], | ||
), | ||
) | ||
class PatternInstanceViewSet(CoreViewSet, ModelViewSet): | ||
http_method_names = ["get", "post", "delete", "head", "options"] | ||
queryset = PatternInstance.objects.all() | ||
serializer_class = PatternInstanceSerializer | ||
|
||
|
||
@extend_schema_view( | ||
list=extend_schema( | ||
description="Retrieve information about all automations created by the service.", | ||
examples=[examples.automation_response], | ||
), | ||
retrieve=extend_schema( | ||
description="Retrieve information about a single automation by ID.", | ||
examples=[examples.automation_response], | ||
), | ||
) | ||
class AutomationViewSet(CoreViewSet, ModelViewSet): | ||
http_method_names = ["get", "delete", "head", "options"] | ||
queryset = Automation.objects.all() | ||
serializer_class = AutomationSerializer | ||
|
||
|
||
@extend_schema(exclude=True) | ||
@api_view(["GET"]) | ||
def ping(request): | ||
def ping(request: Request) -> Response: | ||
return Response(data={"status": "ok"}, status=200) | ||
|
||
|
||
@extend_schema(exclude=True) | ||
@api_view(["GET"]) | ||
def test(request): | ||
def test(request: Request) -> Response: | ||
return Response(data={"hello": "world"}, status=200) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.