Skip to content
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

Api v2 get paginated tasks #705

Merged
merged 30 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
25f8a22
[CLEAN] Removed unnecessary variable name
c8y3 Jan 24, 2025
5e87a9d
Added first implementation of GET /api/v2/cases/{case_identifier}/tasks
c8y3 Jan 24, 2025
a51a1e2
GET /api/v2/cases/{case_identifier}/tasks return [] data field when t…
c8y3 Jan 24, 2025
48e3922
Single quotes
c8y3 Jan 24, 2025
05b1594
Added field total when getting paginated tasks
c8y3 Jan 24, 2025
42b2a85
[ADD] parameters page, per_page, order_by and sort_dir to endpoint GE…
c8y3 Jan 29, 2025
95c59e1
[CLEAN] Reuse parse_pagination_parameters
c8y3 Jan 29, 2025
463ee8d
[CLEAN] Reuse parse_pagination_parameters
c8y3 Jan 29, 2025
4db5130
[CLEAN] Pushed PaginationParameter down into db layer when filtering …
c8y3 Jan 29, 2025
188eb50
[CLEAN] Use parse_pagination_parameters once more
c8y3 Jan 29, 2025
26dd22b
[CLEAN] Pushed down the use of PaginationParameters when filtering fo…
c8y3 Jan 29, 2025
c4a3992
[CLEAN] Added method tasks_filter at the business level
c8y3 Jan 29, 2025
67d07ab
[CLEAN] Factored code to convert the sort direction
c8y3 Jan 29, 2025
92334a0
Added current_page when filtering tasks
c8y3 Jan 29, 2025
94773db
Get paginated tasks returns uuid correctly
c8y3 Jan 29, 2025
79fe615
Building docker in a job before during CI
c8y3 Jan 29, 2025
e8dc5de
Added tag iriswebapp_db/develop when building docker in CI
c8y3 Jan 29, 2025
3247fda
Added build of nginx
c8y3 Jan 29, 2025
899baf6
Added build of app
c8y3 Jan 29, 2025
ae761b0
Moved out execution of API test in its own job
c8y3 Jan 29, 2025
7b2eaa6
Removed unnecessary step in CI
c8y3 Jan 29, 2025
de90cf4
Moved out graphQL generation
c8y3 Jan 29, 2025
de5c81c
Create .env file again :(
c8y3 Jan 29, 2025
57bcbd5
Removed unnecessary steps in CI
c8y3 Jan 29, 2025
b7c1a33
Simplified CI somewhat
c8y3 Jan 31, 2025
9505e03
Moved build of user interface in its own job
c8y3 Jan 31, 2025
b0ee091
Was incorrect cache-dependency-path
c8y3 Jan 31, 2025
09c3f7c
Build UI before e2e tests again. Seems not to be working otherwise
c8y3 Jan 31, 2025
fc5b8b0
[CLEAN] Factored paginated returns
c8y3 Jan 31, 2025
60e1054
Deprecated GET /case/tasks/list in favour of GET /api/v2/cases/<int:c…
c8y3 Jan 31, 2025
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
198 changes: 168 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,174 @@ jobs:
args: check --output-format=github
src: ./source

tests:
name: API tests
build-docker-db:
name: Build docker db
runs-on: ubuntu-22.04
steps:
- name: Check out iris
uses: actions/checkout@v4
- name: Build dockers
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and export
uses: docker/build-push-action@v6
with:
context: docker/db
tags: iriswebapp_db:develop
outputs: type=docker,dest=${{ runner.temp }}/iriswebapp_db.tar
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: iriswebapp_db
path: ${{ runner.temp }}/iriswebapp_db.tar

build-docker-nginx:
name: Build docker nginx
runs-on: ubuntu-22.04
steps:
- name: Check out iris
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and export
uses: docker/build-push-action@v6
with:
context: docker/nginx
tags: iriswebapp_nginx:develop
build-args: |
NGINX_CONF_GID=1234
NGINX_CONF_FILE=nginx.conf
outputs: type=docker,dest=${{ runner.temp }}/iriswebapp_nginx.tar
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: iriswebapp_nginx
path: ${{ runner.temp }}/iriswebapp_nginx.tar

build-docker-app:
name: Build docker app
runs-on: ubuntu-22.04
steps:
- name: Check out iris
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and export
uses: docker/build-push-action@v6
with:
context: .
file: docker/webApp/Dockerfile
tags: iriswebapp_app:develop
outputs: type=docker,dest=${{ runner.temp }}/iriswebapp_app.tar
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: iriswebapp_app
path: ${{ runner.temp }}/iriswebapp_app.tar

build-graphql-documentation:
name: Generate graphQL documentation
runs-on: ubuntu-22.04
needs:
- build-docker-db
- build-docker-nginx
- build-docker-app
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: iriswebapp_*
path: ${{ runner.temp }}
merge-multiple: true
- name: Load docker images
run: |
docker load --input ${{ runner.temp }}/iriswebapp_db.tar
docker load --input ${{ runner.temp }}/iriswebapp_nginx.tar
docker load --input ${{ runner.temp }}/iriswebapp_app.tar
- name: Check out iris
uses: actions/checkout@v4
- name: Start development server
run: |
# TODO using the environment file from tests to build here.
# I am a bit uneasy with this choice.
# For now this works, but if we come to have different .env files for different tests, it won't anymore.
# Maybe the .env should be split to differentiate the variables used during the build from the variables used at runtime,
# or maybe the docker building phase should also be part of the tests
# and we should build different dockers according to the scenarios? This sounds like an issue to me...
# Even though, we use --env-file option when running docker compose, this is still necessary, because the compose has a env_file attribute :(
# TODO should move basic.env file, which is in directory tests, up. It's used in several places. Maybe, rename it into dev.env
cp tests/data/basic.env .env
docker compose --file docker-compose.dev.yml build
docker compose --file docker-compose.dev.yml --env-file tests/data/basic.env up --detach
- name: Generate GraphQL documentation
run: |
npx spectaql@^3.0.2 source/spectaql/config.yml
- name: Stop development server
run: |
docker compose down
- uses: actions/upload-artifact@v4
with:
name: GraphQL DFIR-IRIS documentation
path: public
if-no-files-found: error

test-api:
name: Test API
runs-on: ubuntu-22.04
needs:
- build-docker-db
- build-docker-nginx
- build-docker-app
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: iriswebapp_*
path: ${{ runner.temp }}
merge-multiple: true
- name: Load docker images
run: |
docker load --input ${{ runner.temp }}/iriswebapp_db.tar
docker load --input ${{ runner.temp }}/iriswebapp_nginx.tar
docker load --input ${{ runner.temp }}/iriswebapp_app.tar
- name: Check out iris
uses: actions/checkout@v4
- name: Start development server
run: |
# Even though, we use --env-file option when running docker compose, this is still necessary, because the compose has a env_file attribute :(
# TODO should move basic.env file, which is in directory tests, up. It's used in several places. Maybe, rename it into dev.env
cp tests/data/basic.env .env
docker compose --file docker-compose.dev.yml up --detach
- name: Run tests
working-directory: tests
run: |
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
PYTHONUNBUFFERED=true python -m unittest --verbose
- name: Stop development server
run: |
docker compose down

test-e2e:
name: End to end tests
runs-on: ubuntu-22.04
needs:
- build-docker-db
- build-docker-nginx
- build-docker-app
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: iriswebapp_*
path: ${{ runner.temp }}
merge-multiple: true
- name: Load docker images
run: |
docker load --input ${{ runner.temp }}/iriswebapp_db.tar
docker load --input ${{ runner.temp }}/iriswebapp_nginx.tar
docker load --input ${{ runner.temp }}/iriswebapp_app.tar
- name: Check out iris
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
Expand All @@ -61,30 +213,20 @@ jobs:
run: |
npm ci
npm run build
- name: Run tests
working-directory: tests
run: |
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
docker compose --file ../docker-compose.dev.yml --env-file data/basic.env up --detach --wait
PYTHONUNBUFFERED=true python -m unittest --verbose
docker compose down
- name: Start development server
run: |
docker compose --file docker-compose.dev.yml up --detach
- name: Install e2e dependencies
working-directory: e2e
run: npm ci
- name: Install playwright dependencies
working-directory: e2e
run: npx playwright install chromium firefox
- name: Start development server
run: |
# TODO should move basic.env file, which is in directory tests, up. It's used in several places. Maybe, rename it into dev.env
cp tests/data/basic.env .env
docker compose --file docker-compose.dev.yml up --detach
- name: Run end to end tests
working-directory: e2e
run: npx playwright test
- name: Generate GraphQL documentation
run: |
npx spectaql@^3.0.2 source/spectaql/config.yml
- name: Stop development server
run: |
docker compose down
Expand All @@ -93,8 +235,4 @@ jobs:
with:
name: playwright-report
path: e2e/playwright-report/
- uses: actions/upload-artifact@v4
with:
name: GraphQL DFIR-IRIS documentation
path: public
if-no-files-found: error

1 change: 1 addition & 0 deletions source/app/blueprints/rest/case/case_tasks_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@


@case_tasks_rest_blueprint.route('/case/tasks/list', methods=['GET'])
@endpoint_deprecated('GET', '/api/v2/cases/<int:case_identifier>/tasks')
@ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
@ac_api_requires()
def case_get_tasks(caseid):
Expand Down
23 changes: 23 additions & 0 deletions source/app/blueprints/rest/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from functools import wraps
from flask_sqlalchemy.pagination import Pagination

from app import app
from app.blueprints.responses import response_error, response
Expand All @@ -28,6 +29,28 @@ def response_api_success(data):
return response(200, data=data)


def _get_next_page(paginated_elements: Pagination):
if paginated_elements.has_next:
next_page = paginated_elements.has_next
else:
next_page = None
return next_page


def response_api_paginated(schema, paginated_elements: Pagination):
data = schema.dump(paginated_elements.items, many=True)
next_page = _get_next_page(paginated_elements)

result = {
'total': paginated_elements.total,
'data': data,
'last_page': paginated_elements.pages,
'current_page': paginated_elements.page,
'next_page': next_page
}

return response_api_success(result)

def response_api_deleted():
return response(204)

Expand Down
16 changes: 6 additions & 10 deletions source/app/blueprints/rest/manage/manage_cases_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from app.blueprints.access_controls import ac_api_return_access_denied
from app.blueprints.responses import response_error
from app.blueprints.responses import response_success
from app.blueprints.rest.parsing import parse_pagination_parameters
from app.business.cases import cases_delete
from app.business.cases import cases_update
from app.business.cases import cases_create
Expand All @@ -81,11 +82,9 @@ def get_case_api(cur_id):
@ac_api_requires()
def manage_case_filter() -> Response:

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
pagination_parameters = parse_pagination_parameters(request)

case_ids_str = request.args.get('case_ids', None, type=str)
order_by = request.args.get('order_by', type=str)
sort_dir = request.args.get('sort_dir', 'asc', type=str)

if case_ids_str:
try:
Expand All @@ -112,6 +111,8 @@ def manage_case_filter() -> Response:
draw = 1

filtered_cases = get_filtered_cases(
current_user.id,
pagination_parameters,
case_ids=case_ids_str,
case_customer_id=case_customer_id,
case_name=case_name,
Expand All @@ -124,12 +125,7 @@ def manage_case_filter() -> Response:
case_soc_id=case_soc_id,
start_open_date=start_open_date,
end_open_date=end_open_date,
search_value=search_value,
page=page,
per_page=per_page,
current_user_id=current_user.id,
sort_by=order_by,
sort_dir=sort_dir
search_value=search_value
)
if filtered_cases is None:
return response_error('Filtering error')
Expand Down
12 changes: 12 additions & 0 deletions source/app/blueprints/rest/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from app.models.pagination_parameters import PaginationParameters


def parse_comma_separated_identifiers(identifiers: str):
return [int(identifier) for identifier in identifiers.split(',')]
Expand All @@ -27,3 +29,13 @@ def parse_boolean(parameter: str):
if parameter == 'false':
return False
raise ValueError(f'Expected true or false, got {parameter}')


def parse_pagination_parameters(request) -> PaginationParameters:
arguments = request.args
page = arguments.get('page', 1, type=int)
per_page = arguments.get('per_page', 10, type=int)
order_by = arguments.get('order_by', type=str)
sort_dir = arguments.get('sort_dir', 'asc', type=str)

return PaginationParameters(page, per_page, order_by, sort_dir)
Loading