Skip to content

Commit

Permalink
Implementation of search and getPlugins API
Browse files Browse the repository at this point in the history
  • Loading branch information
Ushmajit committed Apr 25, 2022
1 parent 7c9b2f6 commit 067d205
Show file tree
Hide file tree
Showing 8 changed files with 565 additions and 55 deletions.
89 changes: 65 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,73 @@
# template-nodejs
A template project for nodejs. Has integrated linting, testing,
coverage, reporting, GitHub actions for publishing to npm repository, dependency updates and other goodies.

Easily use this template to quick start a production ready nodejs project template.
# plugin-registry-backend
Plugin Registry Backend service is a express JS service which supports clients
like Brackets Code Editor (https://brackets.io/) and Phoenix Code Editor(https://phoenix.core.ai/)
by providing all the necessary APIs to render the in-app extension store.

## Code Guardian
[![<app> build verification](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml/badge.svg)](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml)

<a href="https://sonarcloud.io/summary/new_code?id=aicore_template-nodejs-ts">
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=alert_status" alt="Sonar code quality check" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=security_rating" alt="Security rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=vulnerabilities" alt="vulnerabilities" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=coverage" alt="Code Coverage" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=bugs" alt="Code Bugs" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=reliability_rating" alt="Reliability Rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=sqale_rating" alt="Maintainability Rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=ncloc" alt="Lines of Code" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=sqale_index" alt="Technical debt" />
[![<app> build verification](https://github.com/aicore/plugin-registry-backend/actions/workflows/build_verify.yml/badge.svg)](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml)

<a href="https://sonarcloud.io/summary/new_code?id=aicore_plugin-registry-backend">
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=alert_status" alt="Sonar code quality check" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=security_rating" alt="Security rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=vulnerabilities" alt="vulnerabilities" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=coverage" alt="Code Coverage" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=bugs" alt="Code Bugs" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=reliability_rating" alt="Reliability Rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=sqale_rating" alt="Maintainability Rating" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=ncloc" alt="Lines of Code" />
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_plugin-registry-backend&metric=sqale_index" alt="Technical debt" />
</a>


# TODOs after template use
1. Update package.json with your app defaults
2. Check Build actions on pull requests.
3. In sonar cloud, enable Automatic analysis from `Administration
Analysis Method` for the first time before a pull request is raised: ![image](https://user-images.githubusercontent.com/5336369/148695840-65585d04-5e59-450b-8794-54ca3c62b9fe.png)
4. Check codacy runs on pull requests, set codacy defaults. You may remove codacy if sonar cloud is only needed.
5. Update the above Code Guardian badges; change all `id=aicore_template-nodejs-ts` to the sonar id of your project fields.
# API Documentation

## SEARCH API

This API supports text-based search queries (see below for detailed params) on plugin registry. Search API can retrieve plugins(Extensions/Themes) by searching titles, keywords or authornames(by default). We can also search on more fields apart from the previously mentioned by passing it as a param. This API can be a functional entry point for all the UI search-box usecases.

### Parameters

* **Required Parameters**

* **clientID {String}**: A unique identifier required for authenticating clients
* **query {String}**: String containing the text to be searched
* **Optional Parameters**

* **filters {Object} (Default: Empty)**: Object contaning the fields array and sortBy field. See below e.g

* **fields {String Array}**: Array containing additional fields to be searched.
* **sortyBy {String}**: Constant value(asc/desc) to sort the results by total number of downloads.
E.g,

``` const filter = { fields:['metadata.author.name','metadata.author.email'], sortBy:'desc'}```
* **resultSize {Integer} (Default: 10)** : SearchResults list size.
* **skipIndex {Integer} {Default: 0}**: Integer value required for pagination

### Sample Request

``` curl -d '{"clientID":"your_iD","query":"Python"}' -H "Content-Type: application/json" http://localhost:3000/search ```

## getPlugins API

This API can be used to retrieve plugins by assetType(Extension/Theme) (see below for detailed params) on plugin registry. We can also apply certain keywords as filters to refine our search results. This API can be a functional entry point for loading plugins by default in the UI.r all the UI search-box usecases.

### Parameters

* **Required Parameters**

* **clientID {String}**: A unique identifier required for authenticating clients
* **assetType {String}**: accepted values are 'EXTENSION' or 'THEME'
* **Optional Parameters**

* **filters {Object} (Default: Empty)**: Object contaning the fields array and sortBy field. See below e.g

* **keywords {String Array}**: Array containing additional keywords to match.
* **sortyBy {String}**: Constant value(asc/desc) to sort the results by total number of downloads.
E.g,

``` const filter = { keywords:['HTML','HTML5'], sortBy:'desc'}```
* **resultSize {Integer} (Default: 10)** : SearchResults list size.
* **skipIndex {Integer} {Default: 0}**: Integer value required for pagination

# Commands available

Expand Down
24 changes: 16 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"name": "@aicore/template-nodejs",
"name": "@aicore/plugin-registry-backend",
"version": "1.0.4",
"description": "Template for nodejs with unit gulp build, test, coverage, code guardian, github and Other defaults",
"main": "index.js",
"main": "src/app.js",
"type": "module",
"keywords": [
"template",
"backend",
"RestAPI",
"API",
"nodejs",
"unit",
"testing",
Expand Down Expand Up @@ -36,14 +38,14 @@
],
"repository": {
"type": "git",
"url": "git+https://github.com/aicore/template-nodejs.git"
"url": "git+https://github.com/aicore/plugin-registry-backend.git"
},
"author": "Arun, core.ai",
"author": "Ushmajit, core.ai",
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://github.com/aicore/template-nodejs/issues"
"url": "https://github.com/aicore/plugin-registry-backend/issues"
},
"homepage": "https://github.com/aicore/template-nodejs#readme",
"homepage": "https://github.com/aicore/plugin-registry-backend#readme",
"devDependencies": {
"@commitlint/cli": "16.2.1",
"@commitlint/config-conventional": "16.2.1",
Expand All @@ -53,5 +55,11 @@
"husky": "7.0.4",
"mocha": "9.2.1"
},
"dependencies": {}
"dependencies": {
"@aicore/elasticsearch-lib": "^1.0.1",
"cors": "^2.8.5",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"superagent": "^7.1.1"
}
}
148 changes: 148 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* GNU AGPL-3.0 License
*
* Copyright (c) 2021 - present core.ai . All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
*
*/
import './env.js';
import express from 'express';
import cors from 'cors';
import requestValidator from './request_validator.js';
import searchHelper from './elastic_search_helper.js';
import npmHelper from './npm_helper.js';

const app = express();
const port = 3000;
let filters = {};
let resultSize = 10;
let skipIndex = 0;

app.use(express.json());

app.use(cors({
methods: ['GET', 'POST']
}));

/**
* SEARCH API : This API supports text-based search queries (see below for detailed params)
* on plugin registry. Search API can retrieve plugins(Extensions/Themes) by searching titles,
* keywords or authornames(by default). We can also search on more fields apart from the previously mentioned
* by passing it as a param. This API can be a functional entry point for all the UI search-box usecases.
*
* Parameters:
* Required:
* 1. clientID {String}: A unique identifier required for authenticating clients
* 2. query {String}: String containing the text to be searched
*
* Optional:
* 1. filters {Object} (Default: Empty) : Object contaning the fields array and sortBy field. See below e.g
* a. fieldsArray {String Array}: Array containing additional fields to be searched
* b. sortyBy {String}: Constant value(asc/desc) to sort the results by total
* number of downloads.
* E.g const filter = { fields:['metadata.author.name','metadata.author.email'], sortBy:'desc'}
* 2. resultSize {Integer} (Default: 10) : SearchResults list size
* 3. skipIndex {Integer} {Default: 0} : Integer value required for pagination
*
*/
app.post('/search', async function(req, res) {
let validationResponse = await requestValidator.validateSearchRequest(req);

res.set({
'Access-Control-Allow-Origin': '*'
});

if (!validationResponse.isValid) {
res.status(validationResponse.statusCode);
res.json({error: validationResponse.errorMessage})
return ;
}

const searchQuery = req.body["query"];
assignOptionalParameters(req);

try {
let searchResponse = await searchHelper.performTextSearch(searchQuery, filters, resultSize, skipIndex);
let modifiedResponse = await npmHelper.updateDownloadStats(searchResponse);
console.log("Success: Search API Request succeeded with no errors, Please check logs for response");
res.status(200);
res.json(modifiedResponse);
} catch (error) {
res.status(500);
res.json({error: JSON.stringify(error)});
}
});

/**
* getPlugins API : This API can be used to retrieve plugins by assetType(Extension/Theme) (see below for detailed params)
* on plugin registry. We can also apply certain keywords as filters to refine our search results.
* This API can be a functional entry point for loading plugins by default in the UI.
*
* Parameters:
* Required:
* 1. clientID {String}: A unique identifier required for authenticating clients
* 2. assetType {String}: accepted values are 'EXTENSION' or 'THEME'
*
* Optional:
* 1. filters {Object} (Default: Empty) : Object contaning the fields array and sortBy field. See below e.g
* a. keywordsArray {String Array}: Array containing additional keywords to match
* b. sortyBy {String}: Constant value(asc/desc) to sort the results by total
* number of downloads.
* E.g const filter = { keywords:['HTML', 'HTML5'], sortBy:'desc'}
* 2. resultSize {Integer} (Default: 10) : SearchResults list size
* 3. skipIndex {Integer} {Default: 0} : Integer value required for pagination
*
*/
app.post('/getPlugins', async function(req, res) {
let validationResponse = await requestValidator.validateGetPluginsRequest(req);

res.set({
'Access-Control-Allow-Origin': '*'
});

if (!validationResponse.isValid) {
res.status(validationResponse.statusCode);
res.json({error: validationResponse.errorMessage})
return ;
}

const assetType = req.body["assetType"];
assignOptionalParameters(req);

try {
let pluginsResponse = await searchHelper.getPlugins(assetType, filters, resultSize, skipIndex);
let modifiedResponse = await npmHelper.updateDownloadStats(pluginsResponse);
console.log("Success: GetPlugins API Request succeeded with no errors, Please check logs for response");
res.status(200);
res.json(modifiedResponse);
} catch (error) {
res.status(500);
res.json({error: JSON.stringify(error)});
}
});

app.listen(port, () => {
console.log(`Plugin-Registry-Backend server listening at http://localhost:${port}`);
});

async function assignOptionalParameters(req) {
if (req.body.hasOwnProperty('filters')) {
filters = req.body["filters"];
}
if (req.body.hasOwnProperty("resultSize")) {
resultSize = req.body.resultSize;
}
if (req.body.hasOwnProperty("skipIndex")) {
resultSize = req.body.resultSize;
}
}
Loading

0 comments on commit 067d205

Please sign in to comment.