diff --git a/README.md b/README.md index 2724719..7c999b1 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,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) @@ -63,7 +64,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 @@ -74,10 +75,7 @@ curl --request GET \ "Jenkins", "TeamCity" ], - "origins": { - "mobile": 22, - "desktop": 35 - }, + "origins": 22, "category": "CI" }, { @@ -86,10 +84,7 @@ curl --request GET \ "Cloudflare", "Arsys Domain Parking" ], - "origins": { - "mobile": 14, - "desktop": 8 - }, + "origins": 14, "category": "Domain parking" } ] @@ -213,6 +208,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 @@ -248,6 +244,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 @@ -291,6 +288,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 @@ -338,6 +336,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 @@ -399,6 +398,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 @@ -588,7 +588,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: @@ -608,7 +608,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: @@ -643,7 +643,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 f84cd9b..38f5d48 100644 --- a/src/__tests__/routes.test.js +++ b/src/__tests__/routes.test.js @@ -233,6 +233,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 6a76918..84f7b79 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.split(',').map(f => f.trim()) || ['category', 'description', 'technologies', 'origins']; query = query.select(...requestedFields); } @@ -56,7 +62,9 @@ const listCategories = async (req, res) => { // Include onlyname and fields in cache key calculation const customCacheKeyData = { onlyname: req.query.onlyname || false, - fields: req.query.fields + fields: req.query.fields, + category: req.query.category ? validateArrayParameter(req.query.category, 'category') : [], + client: req.query.client || 'mobile', }; await executeQuery(req, res, 'categories', queryBuilder, dataProcessor, customCacheKeyData); diff --git a/src/controllers/reportController.js b/src/controllers/reportController.js index 9f24123..14ffaf7 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, @@ -55,7 +54,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)); @@ -78,8 +77,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; @@ -91,7 +97,8 @@ const createReportController = (reportType) => { const queryFilters = { geo: params.geo, rank: params.rank, - technology: techArray, + technology: technologiesArray, + version: versionsArray, startDate: startDate, endDate: params.end }; @@ -113,21 +120,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/src/controllers/technologiesController.js b/src/controllers/technologiesController.js index 5d75aba..1858a8a 100644 --- a/src/controllers/technologiesController.js +++ b/src/controllers/technologiesController.js @@ -69,7 +69,9 @@ const listTechnologies = async (req, res) => { // Include onlyname and fields in cache key calculation const customCacheKeyData = { onlyname: req.query.onlyname || false, - fields: req.query.fields + fields: req.query.fields, + technology: req.query.technology ? validateTechnologyArray(req.query.technology) : [], + category: req.query.category ? validateArrayParameter(req.query.category, 'category') : [], }; await executeQuery(req, res, 'technologies', queryBuilder, dataProcessor, customCacheKeyData); diff --git a/terraform/dev/main.tf b/terraform/dev/main.tf index db15672..87f7c6b 100644 --- a/terraform/dev/main.tf +++ b/terraform/dev/main.tf @@ -98,6 +98,13 @@ paths: responses: 200: description: String + /v1/versions: + get: + summary: versions + operationId: getVersions + responses: + 200: + description: String /v1/audits: get: summary: audits diff --git a/terraform/dev/variables.tf b/terraform/dev/variables.tf index 77e6d54..5869e96 100644 --- a/terraform/dev/variables.tf +++ b/terraform/dev/variables.tf @@ -15,7 +15,7 @@ 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" } variable "google_service_account_cloud_functions" {