Uploaded image for project: 'FlightPath'
  1. FlightPath
  2. FLPATH-2985

Implement CMMO-compatible Sources API endpoints and DTOs

XMLWordPrintable

    • Icon: Story Story
    • Resolution: Unresolved
    • Icon: Undefined Undefined
    • None
    • None
    • insights-on-prem
    • None

      Implement minimal support for CMMO (Cost Management Metrics Operator) in the Koku Sources API. This includes creating new endpoints, adding source type mappings, supporting CMMO's filter[field] query syntax, and wrapping responses in the expected format. The goal is to allow CMMO to register and query sources via Koku's Sources API without requiring the full sources-api-go service.


      Background

      CMMO uses numeric string IDs ("1", "2", "3", "4") for source types while Koku uses provider constants ("OCP", "AWS", "Azure", "GCP"). CMMO also expects:

      • Specific API path: /api/sources/v1.0/
      • Response format: {{"meta": {"count": N}

        , "data": [...]}}

      • filter[field] query parameter syntax
      • Endpoints for /source_types, /application_types, and /applications

      Implementation Tasks

      1. Create Source Type Mapping Module

      Create a new mapping module to convert between CMMO's numeric string source type IDs and Koku's provider type constants.

      Technical Details:

      • Create new file: koku/sources/api/source_type_mapping.py
      • Define three dictionaries:
        • SOURCE_TYPE_ID_TO_TYPE: Maps "1" → "OCP", "2" → "AWS", etc.
        • SOURCE_TYPE_TO_ID: Reverse mapping (auto-generated from above)
        • SOURCE_TYPE_ID_TO_NAME: Maps "1" → "openshift", "2" → "amazon", etc.
      • Use hardcoded values (no configuration needed)

      2. Implement /source_types Endpoint

      Create endpoint that returns a list of source types with their IDs and names.

      Technical Details:

      • Endpoint: GET /api/sources/v1.0/source_types
      • Support query parameter: filter[name]=openshift
      • Response format: {{"meta": {"count": N}

        , "data": [

        {"id": "1", "name": "openshift"}

        , ...]}}

      • Return hardcoded list of source types (no database queries needed)
      • Source type name must be "openshift" (lowercase), not "ocp"

      Files to Create:

      • koku/sources/api/source_type_views.py

      3. Implement /application_types Endpoint

      Create endpoint that returns application types. Since CMMO only supports Cost Management application (hardcoded as ID "0"), this endpoint returns a single hardcoded entry.

      Technical Details:

      • Endpoint: GET /api/sources/v1.0/application_types
      • Support query parameter: filter[name]=/insights/platform/cost-management
      • Response format: {{"meta": {"count": 1}

        , "data": [

        {"id": "0", "name": "/insights/platform/cost-management"}

        ]}}

      Files to Create:

      • koku/sources/api/application_type_views.py

      4. Implement /applications Endpoint

      Create endpoint for managing application associations. Since application_type_id is always "0" (Cost Management, hardcoded in CMMO), no database storage is needed.

      Technical Details:

      • Endpoint: GET /api/sources/v1.0/applications - List applications
      • Endpoint: POST /api/sources/v1.0/applications - Create application association
      • Support query parameters: filter[source_id], filter[application_type_id]
      • Response format: {{"meta": {"count": N}

        , "data": [

        {"id": "...", "source_id": "...", "application_type_id": "0"}

        , ...]}}

      • For GET: Return applications with hardcoded application_type_id="0" for all sources
      • For POST: Validate that application_type_id is "0", then return success (no database storage)

      Files to Create:

      • koku/sources/api/application_views.py

      5. Add /api/sources/v1.0/ Route Alias

      Add a route alias to support CMMO's expected API path format while maintaining backward compatibility.

      Technical Details:

      • Existing path: {{ {API_PATH_PREFIX}v1/}} (e.g., /api/v1/sources/)
        * New CMMO path: /api/sources/v1.0/
        * Both paths should route to the same viewset

        Files to Modify:
        * koku/sources/urls.py
        * koku/sources/api/urls.py (add routes for new endpoints)

        ----

        h3. 6. Override SourceFilter to Support filter[field] Syntax

        Extend SourceFilter to support CMMO's filter[field] query parameter syntax while maintaining backward compatibility. Add new filters for source_type_id and source_ref.

        Technical Details:
        * django-filter does NOT natively support filter[field] syntax
        * Override filter_queryset to manually parse filter[field] parameters using querystring_parser
        * Support both filter[field] and direct field syntax (backward compatible)
        * Add source_type_id filter that maps CMMO ID to Koku provider type
        * Add source_ref filter that maps to authentication_credentials_cluster_id JSON field lookup

        Files to Modify:
        * koku/sources/api/view.py (SourceFilter class)

        ----

        h3. 7. Modify AdminSourcesSerializer to Accept source_type_id in POST

        Update the serializer to accept source_type_id (CMMO format) in POST requests and convert it to source_type (Koku format) for database storage.

        Technical Details:
        * Map source_type_idsource_type using SOURCE_TYPE_ID_TO_TYPE
        * Accept both source_type and source_type_id (backward compatible)
        * Handle source_ref by storing in authentication.credentials.cluster_id for OCP sources

        Files to Modify:
        * koku/sources/api/serializers.py (AdminSourcesSerializer class)

        ----

        h3. 8. Add source_type_id and source_ref to SourcesSerializer Responses

        Update the serializer to include source_type_id and source_ref fields in GET responses.

        Technical Details:
        * source_type_id: Convert Koku's source_type ("OCP") to CMMO's numeric ID ("1")
        * source_ref: Extract from authentication.credentials.cluster_id JSON field for OCP sources
        * Both fields should be read-only (SerializerMethodField)

        Files to Modify:
        * koku/sources/api/serializers.py (SourcesSerializer class)

        ----

        h3. 9. Wrap List Responses with meta.count Format

        Update the SourcesViewSet to wrap all list responses in CMMO's expected format.

        Technical Details:
        * CMMO expects responses in format: "meta": {"count": N}, "data": [...]
        * Override SourcesViewSet.list() method
        * Wrap response data in expected format
        * Apply to all list endpoints (sources list, filtered results, etc.)

        Files to Modify:
        * koku/sources/api/view.py (SourcesViewSet class)

        ----

        h2. Files Summary

        Files to Create:
        * koku/sources/api/source_type_mapping.py
        * koku/sources/api/source_type_views.py
        * koku/sources/api/application_type_views.py
        * koku/sources/api/application_views.py

        Files to Modify:
        * koku/sources/urls.py
        * koku/sources/api/urls.py
        * koku/sources/api/view.py
        * koku/sources/api/serializers.py

        ----

        h2. Acceptance Criteria

        h3. Source Type Mapping
        * ( ) File koku/sources/api/source_type_mapping.py exists with all mappings
        * ( ) SOURCE_TYPE_ID_TO_TYPE correctly maps all 4 provider types ("1"→"OCP", "2"→"AWS", "3"→"Azure", "4"→"GCP")
        * ( ) SOURCE_TYPE_TO_ID correctly reverse maps all provider types
        * ( ) SOURCE_TYPE_ID_TO_NAME includes "openshift" (lowercase) for ID "1"

        h3. /source_types Endpoint
        * ( ) GET /api/sources/v1.0/source_types returns list of source types
        * ( ) Response includes meta.count and data array
        * ( ) Each source type has id (string) and name (string) fields
        * ( ) filter[name]=openshift returns only OpenShift source type
        * ( ) All 4 provider types are included

        h3. /application_types Endpoint
        * ( ) GET /api/sources/v1.0/application_types returns single application type
        * ( ) Response includes meta.count (value: 1) and data array
        * ( ) Application type has id: "0" and name: "/insights/platform/cost-management"
        * ( ) filter[name]=/insights/platform/cost-management returns the entry

        h3. /applications Endpoint
        * ( ) GET /api/sources/v1.0/applications returns list of applications
        * ( ) GET /api/sources/v1.0/applications?filter[source_id]=X filters by source_id
        * ( ) Each application has id, source_id, and application_type_id: "0"
        * ( ) POST /api/sources/v1.0/applications accepts source_id and application_type_id
        * ( ) POST validates application_type_id is "0" (reject others)

        h3. Route Alias
        * ( ) Route /api/sources/v1.0/ is accessible
        * ( ) Existing route {{{API_PATH_PREFIX}

        v1/}} still works

      • ( ) Both routes point to the same endpoints

      Filter Support

      • ( ) filter[source_type_id]=1 correctly filters sources by provider type
      • ( ) filter[source_ref]=uuid correctly filters sources by cluster_id
      • ( ) Existing filters (name, type) still work as direct query parameters
      • ( ) Both filter[field] and direct field syntax work (backward compatible)

      Serializer Updates

      • ( ) POST request with source_type_id: "1" creates source with source_type: "OCP"
      • ( ) POST request with source_type: "OCP" still works (backward compatible)
      • ( ) POST request with source_ref: "uuid" stores value in authentication.credentials.cluster_id for OCP sources
      • ( ) GET responses include source_type_id (correct numeric string based on provider type)
      • ( ) GET responses include source_ref (populated from authentication.credentials.cluster_id for OCP sources, null otherwise)

      Response Format

      • ( ) GET /api/sources/v1.0/sources/ returns {{"meta": {"count": N}

        , "data": [...]}}

      • ( ) meta.count reflects the actual number of items in data array
      • ( ) Empty results return {{"meta": {"count": 0}

        , "data": []}}


      Testing Recommendations

      • Unit tests for mapping module
      • Integration tests for each new endpoint
      • Test backward compatibility for existing API consumers
      • Test filter syntax (both old and new formats)
      • Test serializer validation and response format

      Performance Considerations

      • JSON field filtering (authentication_credentials_cluster_id) is slower than indexed columns
      • Consider adding database index on JSON path if performance becomes an issue
      • Current approach is acceptable for minimal implementation

              rh-ee-ehendler Elkana Hendler
              rh-ee-ehendler Elkana Hendler
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated: