GSA / GSA/data.gov

Organization API rejects valid organization_type values

Open
#6,207 0 comments 0 reactions 0 assignees View on GitHub
API bug
Dominant language
Python
Stars
1.1k
Forks
200
Avg merge
4h 56m
Merged PRs (30d)
5

Description

`POST /api/organization/add` fails with a 400 for any request that includes a valid `organization_type` value (e.g. `"Non-Profit"`), even though the value is one of the documented valid options.

- **Component:** `datagov-harvester` admin app, `app/api/organizations.py` / `app/api_schemas.py`
- **Environment:** reproduced locally (`flask run` against a local Postgres), branch `6180-catalog-metadata`, but the root cause is unrelated to that branch and appears on `main` as well.

**Error logs:**

```
ERROR:root:Error: (builtins.LookupError) 'OrganizationType.Non-Profit' is not among the defined enum values. Enum name: organization_type_enum. Possible values: Federal Gov.., City Govern.., State Gover.., ..., Non-Profit
[SQL: INSERT INTO organization (name, logo, description, slug, organization_type, aliases, id) VALUES (%(name)s::VARCHAR, %(logo)s::VARCHAR, %(description)s::VARCHAR, %(slug)s::VARCHAR, %(organization_type)s, %(aliases)s::VARCHAR[], %(id)s::VARCHAR)]
[parameters: [{'slug': 'opentopography', 'name': 'OpenTopography', 'organization_type': , 'logo': None, 'description': None, 'aliases': None}]]
```

## How to reproduce

1. Start the admin app locally against a local Postgres instance.
2. Send:
```bash
curl -s -X POST http://localhost:8080/api/organization/add -H "X-API-Key: " -H "Content-Type: application/json" -d '{"name": "OpenTopography", "slug": "opentopography", "organization_type": "Non-Profit"}'
```
3. Observe the response and server log.

## Expected behavior

The organization is created successfully with `organization_type` set to `"Non-Profit"`, matching the value sent in the request (same as it works today when `organization_type` is omitted from the payload).

## Actual behavior

The request returns `{"error":"Failed to add organization."}` (HTTP 400). The server log shows the actual underlying `LookupError` above — the insert is rejected by Postgres because the value being bound is the string `"OrganizationType.Non-Profit"` instead of `"Non-Profit"`.

Root cause: `app/api_schemas.py`'s `OrgCreate.organization_type` field is built via `_to_enum("OrganizationType", ORGANIZATION_TYPE_VALUES)`, which deserializes the incoming JSON string into an actual Python `Enum` **member** (`OrganizationType.Non-Profit`), not a plain string. That enum member then flows straight through `deps.add_organization()` → `Organization(**org_data)` without ever calling `.value` on it. The `Organization.organization_type` database column is a plain string-based SQLAlchemy `Enum(*ORGANIZATION_TYPE_VALUES, name="organization_type_enum")` (defined in `datagov-databases`), so when SQLAlchemy binds the Python `Enum` member as a query parameter, it stringifies it via the enum's default `__str__`, producing `"OrganizationType.Non-Profit"` — not a valid value in the Postgres `organization_type_enum` type.

Any request that omits `organization_type` succeeds today, since that fallback path never encounters this conversion at all. This means organizations can currently only be created via this API by leaving `organization_type` unset, or by inserting directly into the database.

## Sketch

- Convert the deserialized enum member back to its `.value` before handing `org_data` to `Organization(...)` — likely in `add_organization_api()` in `app/api/organizations.py`, or inside `deps.add_organization()` — so a plain string reaches the SQLAlchemy `Enum` column, consistent with how the raw column is defined.
- Check whether `edit_organization_api()` / `update_organization()` have the same issue when updating `organization_type` on an existing org.
- Confirm whether the web UI's organization form goes through the same `OrgCreate` schema/path, or a different one — if the same, it's likely affected too and should be covered by the same fix.
- Add a regression test asserting an organization can be created via the API with each valid `organization_type` value, not just with the field omitted.

Contributor guide

Open the contributing guide

Research direction

Start with app/api/organizations.py and app/api_schemas.py, then trace deps.add_organization() into the Organization model and its organization_type column. Reproduce the POST request locally against Postgres, and inspect the edit/update path for the same conversion issue. Done means valid organization_type values create successfully, updates are checked, and regression coverage exercises every valid value.

Written by the indexing model from the issue text.

Assessment

Tech stack
flask, postgresql, python, sqlalchemy
Domain
api, backend, database
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.