diff --git a/README.md b/README.md index a7a66ed..9f0ded2 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Lists available categories. #### Categories Parameters - `category` (optional): Filter by category name(s) - comma-separated list +- `client` (optional): Filter by client type (e.g., `mobile`, `desktop`) - defaults to `mobile` - `onlyname` (optional): If present, returns only category names - `fields` (optional): Comma-separated list of fields to include in the response (see [Field Selection API Documentation](#field-selection-api-documentation) for details) @@ -62,7 +63,7 @@ Lists available categories. ```bash curl --request GET \ - --url 'https://d{{HOST}}/v1/categories?category=Domain%20parking%2CCI' + --url 'https://{{HOST}}/v1/categories?category=Domain%20parking%2CCI&client=desktop' ``` ```json @@ -73,10 +74,7 @@ curl --request GET \ "Jenkins", "TeamCity" ], - "origins": { - "mobile": 22, - "desktop": 35 - }, + "origins": 22, "category": "CI" }, { @@ -85,10 +83,7 @@ curl --request GET \ "Cloudflare", "Arsys Domain Parking" ], - "origins": { - "mobile": 14, - "desktop": 8 - }, + "origins": 14, "category": "Domain parking" } ] @@ -212,6 +207,7 @@ Provides technology adoption data. - `rank` (required): Filter by rank - `start` (optional): Filter by date range start (YYYY-MM-DD or 'latest') - `end` (optional): Filter by date range end (YYYY-MM-DD) +- `version` (optional): Filter by version name(s) - comma-separated list (only when single technology is specified) #### Adoption Response @@ -247,6 +243,7 @@ Provides Core Web Vitals metrics for technologies. - `rank` (required): Filter by rank - `start` (optional): Filter by date range start (YYYY-MM-DD or 'latest') - `end` (optional): Filter by date range end (YYYY-MM-DD) +- `version` (optional): Filter by version name(s) - comma-separated list (only when single technology is specified) #### CWV Response @@ -290,6 +287,7 @@ Provides Lighthouse scores for technologies. - `rank` (required): Filter by rank - `start` (optional): Filter by date range start (YYYY-MM-DD or 'latest') - `end` (optional): Filter by date range end (YYYY-MM-DD) +- `version` (optional): Filter by version name(s) - comma-separated list (only when single technology is specified) #### Lighthouse Response @@ -337,6 +335,7 @@ Provides Page Weight metrics for technologies. - `rank` (required): Filter by rank - `start` (optional): Filter by date range start (YYYY-MM-DD or 'latest') - `end` (optional): Filter by date range end (YYYY-MM-DD) +- `version` (optional): Filter by version name(s) - comma-separated list (only when single technology is specified) #### Page Weight Response @@ -398,6 +397,7 @@ Provides Lighthouse audits for technologies. - `rank` (required): Filter by rank - `start` (optional): Filter by date range start (YYYY-MM-DD or 'latest') - `end` (optional): Filter by date range end (YYYY-MM-DD) +- `version` (optional): Filter by version name(s) - comma-separated list (only when single technology is specified) #### Audits Response @@ -585,7 +585,7 @@ The categories and technologies endpoints now support custom field selection, al - `category` - Category name - `description` - Category description - `technologies` - Array of technology names in the category -- `origins` - Array of origin companies/organizations +- `origins` - Number of origins Get only category names: @@ -605,7 +605,7 @@ GET /v1/categories?fields=category,description - `category` - Category name - `description` - Technology description - `icon` - Icon filename -- `origins` - Array of origin companies/organizations +- `origins` - Dictionary with origins per client Get only technology names and categories: @@ -640,7 +640,7 @@ GET /v1/technologies?category=JavaScript%20Frameworks&fields=technology,icon - `technology` - Technology name - `version` - Version name -- `origins` - Mobile and desktop origins +- `origins` - Dictionary with origins per client Get only technology and version names: diff --git a/src/__tests__/routes.test.js b/src/__tests__/routes.test.js index 179a361..da5ecc2 100644 --- a/src/__tests__/routes.test.js +++ b/src/__tests__/routes.test.js @@ -255,6 +255,12 @@ describe('API Routes', () => { expect(Array.isArray(res.body)).toBe(true); }); + it('should return adoption data with version parameter', async () => { + const res = await request(app).get('/v1/adoption?technology=WordPress&geo=ALL&rank=ALL&start=latest&version=5.0'); + expect(res.statusCode).toEqual(200); + expect(Array.isArray(res.body)).toBe(true); + }); + it('should handle missing required parameters', async () => { const res = await request(app).get('/v1/adoption'); expect(res.statusCode).toEqual(400); diff --git a/src/controllers/categoriesController.js b/src/controllers/categoriesController.js index 1e20645..69dbe7c 100644 --- a/src/controllers/categoriesController.js +++ b/src/controllers/categoriesController.js @@ -8,7 +8,7 @@ const listCategories = async (req, res) => { const queryBuilder = async (params) => { /* // Validate parameters - const supportedParams = ['category', 'onlyname', 'fields']; + const supportedParams = ['category', 'onlyname', 'fields', 'client']; const providedParams = Object.keys(params); const unsupportedParams = providedParams.filter(param => !supportedParams.includes(param)); @@ -20,6 +20,7 @@ const listCategories = async (req, res) => { */ const isOnlyNames = params.onlyname || typeof params.onlyname === 'string'; + const client = params.client || 'mobile'; // Default client if not provided const hasCustomFields = params.fields && !isOnlyNames; let query = firestore.collection('categories').orderBy('category', 'asc'); @@ -32,11 +33,16 @@ const listCategories = async (req, res) => { } } + // Apply client filter + if (client) { + query = query.where('client', '==', client); + } + // Apply field selection if (isOnlyNames) { query = query.select('category'); } else if (hasCustomFields) { - const requestedFields = params.fields.split(',').map(f => f.trim()); + const requestedFields = params.fields ? params.fields.split(',').map(f => f.trim()) : ['category', 'description', 'technologies', 'origins']; query = query.select(...requestedFields); } diff --git a/src/controllers/reportController.js b/src/controllers/reportController.js index d7371a0..cf13e0c 100644 --- a/src/controllers/reportController.js +++ b/src/controllers/reportController.js @@ -1,5 +1,4 @@ -import { firestoreOld } from '../utils/db.js'; -const firestore = firestoreOld; +import { firestore } from '../utils/db.js'; import { REQUIRED_PARAMS, @@ -52,7 +51,7 @@ const createReportController = (reportType) => { /* // Validate supported parameters - const supportedParams = ['technology', 'geo', 'rank', 'start', 'end']; + const supportedParams = ['technology', 'geo', 'rank', 'start', 'end', 'version']; const providedParams = Object.keys(params); const unsupportedParams = providedParams.filter(param => !supportedParams.includes(param)); @@ -75,8 +74,15 @@ const createReportController = (reportType) => { return; } - // Validate and process technology array - const techArray = validateArrayParameter(params.technology, 'technology'); + // Validate and process technologies + const technologiesArray = validateArrayParameter(params.technology, 'technology'); + + // Validate and process versions + // Apply version filter with special handling for 'ALL' case + let versionsArray = ['ALL']; + if (technologiesArray.length === 1 && params.version) { + versionsArray = validateArrayParameter(params.version, 'version'); + } // Handle 'latest' date substitution let startDate = params.start; @@ -92,21 +98,21 @@ const createReportController = (reportType) => { query = query.where('rank', '==', params.rank); // Apply technology filter with batch processing - query = query.where('technology', 'in', techArray); + query = query.where('technology', 'in', technologiesArray); - // Apply version filter with special handling for 'ALL' case - if (params.version && techArray.length === 1) { - //query = query.where('version', '==', params.version); // TODO: Uncomment when migrating to a new data schema - } else { - //query = query.where('version', '==', 'ALL'); - } + // Apply version filter with batch processing + query = query.where('version', 'in', versionsArray); // Apply date filters if (startDate) query = query.where('date', '>=', startDate); if (params.end) query = query.where('date', '<=', params.end); // Apply field projection to optimize query - query = query.select('date', 'technology', config.dataField); + const selectFields = ['date', 'technology', config.dataField]; + if (!(versionsArray.length === 1 && versionsArray[0] === 'ALL')) { + selectFields.push('version'); + } + query = query.select(...selectFields); // Execute query const snapshot = await query.get(); diff --git a/terraform/dev/variables.tf b/terraform/dev/variables.tf index e864e10..6990e99 100644 --- a/terraform/dev/variables.tf +++ b/terraform/dev/variables.tf @@ -15,5 +15,5 @@ variable "environment" { variable "project_database" { type = string description = "The database name" - default = "tech-report-api-prod" // TODO: Update this to the DEV database name + default = "tech-report-api-dev" } diff --git a/terraform/modules/run-service/variables.tf b/terraform/modules/run-service/variables.tf index 48d3723..9868030 100644 --- a/terraform/modules/run-service/variables.tf +++ b/terraform/modules/run-service/variables.tf @@ -9,6 +9,7 @@ variable "region" { variable "environment" { description = "The 'Environment' that is being created/deployed. Applied as a suffix to many resources." type = string + default = "dev" } variable "service_name" { description = "Optional: Can be used to create more than function from the same package"