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

Dev → Main March 2024 #209

Merged
merged 18 commits into from Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/pull.yaml
Expand Up @@ -12,7 +12,7 @@ jobs:
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
Expand All @@ -30,6 +30,8 @@ jobs:
- uses: psf/black@stable

test_api:
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
name: Test API
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -127,4 +129,4 @@ jobs:
cache-dependency-path: 'client/package-lock.json'
- run: npm ci
- name: Build app
run: npm run build
run: npm run build
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Expand Up @@ -19,5 +19,4 @@ jobs:
- name: Run tests
env:
VUE_APP_PDAP_API_KEY: ${{ secrets.VUE_APP_PDAP_API_KEY }}

run: python regular_api_checks.py
16 changes: 11 additions & 5 deletions README.md
Expand Up @@ -47,22 +47,24 @@ pip install -r requirements.txt

### 5. Add environment secrets

Either add a `.env` file to your local root directory or manually export these secrets: `DO_DATABASE_URL` and `VITE_VUE_APP_BASE_URL`.
Either add a `.env` file to your local root directory or manually export these secrets: `DO_DATABASE_URL` and `VITE_VUE_API_BASE_URL`.

Reach out to contact@pdap.io or make noise in Discord if you'd like access to these keys.

```
# .env

DO_DATABASE_URL="postgres://data_sources_app:<password>@db-postgresql-nyc3-38355-do-user-8463429-0.c.db.ondigitalocean.com:25060/defaultdb"
VITE_VUE_APP_BASE_URL="http://localhost:5000"
VITE_VUE_API_BASE_URL="http://localhost:5000"
VITE_VUE_APP_BASE_URL="http://localhost:8888"
```

```
# shell

export DO_DATABASE_URL=postgres://data_sources_app:<password>@db-postgresql-nyc3-38355-do-user-8463429-0.c.db.ondigitalocean.com:25060/defaultdb
export VITE_VUE_APP_BASE_URL="http://localhost:5000"
export VITE_VUE_API_BASE_URL="http://localhost:5000"
export VITE_VUE_APP_BASE_URL="http://localhost:8888"
```

### 6. Allow your IP address
Expand Down Expand Up @@ -108,14 +110,18 @@ pip install pytest
pytest

```

## Linting
Linting is enforced with black on PR creation. You can use black to automatically reformat your files before commiting them, this will allow your PR to pass this check. Any files that require reformatting will be listed on any failed checks on the PR.
```
black app_test.py
```

## Other helpful commands for the client app
## Client App

A few things to know:

- We use Vue3. This allows for using either the options or composition APIs. Feel free to use whichever you are most fluent in.
- We use `pinia` for state management. This works much better with the composition API than with options, so it is recommended to use the composition API if you need data from one of the `pinia` stores.

### Compiles and minifies for production
```
Expand Down
41 changes: 39 additions & 2 deletions app.py
Expand Up @@ -2,9 +2,17 @@
from flask_restful import Api
from flask_cors import CORS
from resources.User import User
from resources.Login import Login
from resources.RefreshSession import RefreshSession
from resources.ApiKey import ApiKey
from resources.RequestResetPassword import RequestResetPassword
from resources.ResetPassword import ResetPassword
from resources.QuickSearch import QuickSearch
from resources.DataSources import DataSources
from resources.DataSources import DataSourceById
from resources.DataSources import (
DataSources,
DataSourcesNeedsIdentification,
DataSourceById,
)
from resources.Agencies import Agencies
from resources.Archives import Archives
from resources.SearchTokens import SearchTokens
Expand All @@ -19,6 +27,29 @@
api.add_resource(
User, "/user", resource_class_kwargs={"psycopg2_connection": psycopg2_connection}
)
api.add_resource(
Login, "/login", resource_class_kwargs={"psycopg2_connection": psycopg2_connection}
)
api.add_resource(
RefreshSession,
"/refresh-session",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
ApiKey,
"/api_key",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
RequestResetPassword,
"/request-reset-password",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
ResetPassword,
"/reset-password",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
QuickSearch,
"/quick-search/<search>/<location>",
Expand All @@ -34,6 +65,11 @@
"/data-sources",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
DataSourcesNeedsIdentification,
"/data-sources-needs-identification",
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)
api.add_resource(
DataSourceById,
"/data-sources-by-id/<data_source_id>",
Expand All @@ -50,5 +86,6 @@
resource_class_kwargs={"psycopg2_connection": psycopg2_connection},
)


if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
109 changes: 105 additions & 4 deletions app_test.py
Expand Up @@ -9,19 +9,34 @@
)
from middleware.data_source_queries import (
data_sources_query,
data_sources_results,
approved_data_sources,
needs_identification_data_sources,
data_source_by_id_query,
data_source_by_id_results,
DATA_SOURCES_APPROVED_COLUMNS,
)

from middleware.user_queries import (
user_post_results,
user_check_email,
)
from middleware.login_queries import (
login_results,
create_session_token,
token_results,
is_admin,
)
from middleware.archives_queries import (
archives_get_results,
archives_get_query,
archives_put_broken_as_of_results,
archives_put_last_cached_results,
ARCHIVES_GET_COLUMNS,
)
from middleware.reset_token_queries import (
check_reset_token,
add_reset_token,
delete_reset_token,
)
from app_test_data import (
DATA_SOURCES_ROWS,
DATA_SOURCE_QUERY_RESULTS,
Expand Down Expand Up @@ -95,13 +110,19 @@ def test_unaltered_search_query(session):


def test_data_sources(session):
response = data_sources_results(conn=session)
response = approved_data_sources(conn=session)

assert response


def test_needs_identification(session):
response = needs_identification_data_sources(conn=session)

assert response


def test_data_sources_approved(session):
response = data_sources_results(conn=session)
response = approved_data_sources(conn=session)

assert (
len([d for d in response if "https://joinstatepolice.ny.gov/15-mile-run" in d])
Expand All @@ -125,6 +146,86 @@ def test_data_source_by_id_approved(session):
assert not response


def test_user_post_query(session):
curs = session.cursor()
user_post_results(curs, "unit_test", "unit_test")

email_check = curs.execute(
f"SELECT email FROM users WHERE email = 'unit_test'"
).fetchone()[0]

assert email_check == "unit_test"


def test_login_query(session):
curs = session.cursor()
user_data = login_results(curs, "test")

assert user_data["password_digest"]


def test_create_session_token_results(session):
curs = session.cursor()
token = create_session_token(curs, 1, "test")

curs = session.cursor()
new_token = token_results(curs, token)

assert new_token["email"]


def test_is_admin(session):
curs = session.cursor()
admin = is_admin(curs, "mbodenator@gmail.com")

assert admin


def test_not_admin(session):
curs = session.cursor()
admin = is_admin(curs, "test")

assert not admin


def test_user_check_email(session):
curs = session.cursor()
user_data = user_check_email(curs, "test")
print(user_data)

assert user_data["id"]


def test_check_reset_token(session):
curs = session.cursor()
reset_token = check_reset_token(curs, "test")
print(reset_token)

assert reset_token["id"]


def test_add_reset_token(session):
curs = session.cursor()
add_reset_token(curs, "unit_test", "unit_test")

email_check = curs.execute(
f"SELECT email FROM reset_tokens WHERE email = 'unit_test'"
).fetchone()[0]

assert email_check == "unit_test"


def test_delete_reset_token(session):
curs = session.cursor()
delete_reset_token(curs, "test", "test")

email_check = curs.execute(
f"SELECT email FROM reset_tokens WHERE email = 'test'"
).fetchone()

assert not email_check


def test_archives_get_results(session):
response = archives_get_results(conn=session)

Expand Down
51 changes: 51 additions & 0 deletions app_test_data.py
Expand Up @@ -103,6 +103,57 @@
"ok",
"approved",
),
(
"Media Bulletins for ",
"NULL",
"NULL",
"Media Bulletins",
"https://hollister.ca.gov/government/city-departments/police/",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
datetime.datetime(2023, 10, 26, 18, 20, 54, tzinfo=datetime.timezone.utc),
datetime.datetime(2023, 11, 8, 19, 5, 30, tzinfo=datetime.timezone.utc),
"NULL",
"NULL",
"NULL",
'{"id": "usrtLIB4Vr3jTH8Ro", "email": "josh.chamberlain@pdap.io", "name": "Josh Chamberlain"}',
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"NULL",
"needs identification",
),
]
DATA_SOURCE_QUERY_RESULTS = [
(
Expand Down