Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions api/cohorts/sync_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,18 @@
)

_LIST_RESPONSE = inline_serializer(
"AmplitudeListResponse", {"list_id": serializers.UUIDField()}
"AmplitudeListResponse",
{
"list_id": serializers.UUIDField(),
"listId": serializers.UUIDField(),
"response": inline_serializer(
"AmplitudeListResponseEnvelope",
{
"list_id": serializers.UUIDField(),
"listId": serializers.UUIDField(),
},
),
},
)

_MIXPANEL_RESPONSE = inline_serializer(
Expand Down Expand Up @@ -73,7 +84,21 @@ def create(self, request: Request) -> Response:
name=serializer.validated_data["name"],
source_type=CohortSourceType.AMPLITUDE,
)
return Response({"list_id": str(cohort.uuid)})
# Amplitude's Testing tab and production sync worker read the list
# ID from this body differently: the Testing tab wraps the body in a
# {"response": ...} envelope before applying the configured ID path,
# while the production worker appears to ignore the configured path
# and read the documented default key, camelCase "listId". Carrying
# the ID at every spelling and depth satisfies every parser
# observed.
list_id = str(cohort.uuid)
return Response(
{
"list_id": list_id,
"listId": list_id,
"response": {"list_id": list_id, "listId": list_id},
}
)

@action(detail=True, methods=["POST"])
def add(self, request: Request, pk: str) -> Response:
Expand Down
9 changes: 9 additions & 0 deletions api/tests/unit/cohorts/test_sync_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ def test_amplitude_create_list__valid_key__creates_amplitude_cohort(
# Then
assert response.status_code == status.HTTP_200_OK
cohort = Cohort.objects.get(uuid=response.json()["list_id"])
# Amplitude's test and production parsers read the ID under different
# keys and depths; every copy must be present.
body = response.json()
assert (
body["listId"]
== body["response"]["list_id"]
== body["response"]["listId"]
== body["list_id"]
)
assert cohort.environment == key.environment
assert cohort.source_type == CohortSourceType.AMPLITUDE
assert cohort.segment.name == "[Amplitude] Beta users: 1234"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ Attributes:
### `cohorts.sync_webhook.rejected`

Logged at `warning` from:
- `api/cohorts/sync_views.py:214`
- `api/cohorts/sync_views.py:239`

Attributes:
- `action`
Expand Down
19 changes: 19 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18907,7 +18907,26 @@ components:
list_id:
type: string
format: uuid
listId:
type: string
format: uuid
response:
$ref: '#/components/schemas/AmplitudeListResponseEnvelope'
required:
- listId
- list_id
- response
AmplitudeListResponseEnvelope:
type: object
properties:
list_id:
type: string
format: uuid
listId:
type: string
format: uuid
required:
- listId
- list_id
AuditLogList:
type: object
Expand Down
Loading