diff --git a/.gitignore b/.gitignore index 6704566..e4913a7 100644 --- a/.gitignore +++ b/.gitignore @@ -90,7 +90,8 @@ dist # vuepress build output .vuepress/dist - + 419 +Page E # Serverless directories .serverless/ diff --git a/docker-compose.yaml b/docker-compose.yaml index 025c0b8..2f7d2f5 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,4 +12,14 @@ services: - ${LOCAL_PORT}:${NODE_PORT} environment: - NODE_ENV= ${NODE_ENV} - - NODE_PORT= ${NODE_PORT} \ No newline at end of file + - NODE_PORT= ${NODE_PORT} + elasticsearch: + image: ${ES_IMAGE_NAME}:${ES_IMAGE_VERSION} + build: + context: . + dockerfile: Dockerfile + container_name: ${ES_CONTAINER_NAME} + environment: + - xpack.security.enabled=false + - discovery.type=single-node + \ No newline at end of file diff --git a/elastic/ind.papers.json b/elastic/ind.papers.json new file mode 100644 index 0000000..b5cc56a --- /dev/null +++ b/elastic/ind.papers.json @@ -0,0 +1,27 @@ +{ + "mappings": { + "properties": { + "id": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "authors": { + "type": "text" + }, + "summary": { + "type": "text" + }, + "tags": { + "type": "keyword" + }, + "content": { + "type": "text" + }, + "suggested": { + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/elastic/testdoc.json b/elastic/testdoc.json new file mode 100644 index 0000000..cff5269 --- /dev/null +++ b/elastic/testdoc.json @@ -0,0 +1,8 @@ +{ + "id":"eeeb2d01-8315-454e-b33f-3d6caa25db42", + "title":"some title matches the query", + "authors":["Moeid Heidari","Denis Gorbanov"], + "summary":"Lorem Ipsum is simply dummy text of the printing and typesetting industry...", + "tags":["tag1","tag2","tag3"], + "content":"## Introduction\nIn this article we will go through a list of best practices to write a Dockerfile to containerize your projects regardless to the technology and programming language used.\nFirstly lets see why we need to containerize our projects and why we should use docker or in one word why we do need Dockerization (Containerization + Docker).\nAs for Portability we can deploy our containerized application to any other systems where we can find a container engine system installed without being worry about the dependencies and required tools the application depends on. When we prefer containers to virtual machines actually we loook to have a quicker to start, faster to create, and a system with much smaller footprints. These properties of a container system means that it bring a higher performance. We can also be confident about the Agility and responsiveness of your software development process while using containers. When we talk about Isolation again containerization takes our attention as it Isolates what ever required to run the application as a block box. It is also scalable as it is possible to create an unlimitted number of instantiations without any conflict with each other. Overall these properties of a containerized application helps us to make a container orchestration system dream come true in the future.\nDocker as an open platform for developing, shipping, and running applications has almost all the features we expect from a container engine. It is an OS-level virtualization and it has a much smaller footprints compared virtual machines which are a copy of the whole OS kernel. It means docker would use the same kernel on your machine and just makes the application layer for us.\nTo deploy our applications as a docker container we need to write a Dockerfile for the project containing all the configuration to fetch, build and run all the dependencies of our project.\nBelow you can see a Dockerfile example which is the most simple one." +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9d178bc..a40872b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8069,13 +8069,13 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==", - "devOptional": true + "dev": true }, "node_modules/swagger-ui-express": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz", "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==", - "devOptional": true, + "dev": true, "dependencies": { "swagger-ui-dist": ">=4.11.0" }, @@ -10072,8 +10072,7 @@ "@nestjs-addons/in-memory-db": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@nestjs-addons/in-memory-db/-/in-memory-db-3.0.3.tgz", - "integrity": "sha512-E+/7ivsddNwXo+5c0CDBBlnDS7jhMlseOOOApkgxEB79siGeOEqvIIshlSCZF8gc27N4xSJ8WqpFhFZXVJB4Qw==", - "requires": {} + "integrity": "sha512-E+/7ivsddNwXo+5c0CDBBlnDS7jhMlseOOOApkgxEB79siGeOEqvIIshlSCZF8gc27N4xSJ8WqpFhFZXVJB4Qw==" }, "@nestjs/axios": { "version": "0.0.8", @@ -10188,8 +10187,7 @@ "@nestjs/mapped-types": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz", - "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==", - "requires": {} + "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==" }, "@nestjs/platform-express": { "version": "8.4.7", @@ -10888,8 +10886,7 @@ "@willsoto/nestjs-prometheus": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@willsoto/nestjs-prometheus/-/nestjs-prometheus-4.6.0.tgz", - "integrity": "sha512-T5OaGZBbmEoyRAnwTGWiTu99eVpu49j0yuwPuRg4Lr3PnTdW0G7KifdByohP1x0BGTXWdAvsRTZ69k6wKJIf7A==", - "requires": {} + "integrity": "sha512-T5OaGZBbmEoyRAnwTGWiTu99eVpu49j0yuwPuRg4Lr3PnTdW0G7KifdByohP1x0BGTXWdAvsRTZ69k6wKJIf7A==" }, "@xtuc/ieee754": { "version": "1.2.0", @@ -10922,15 +10919,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "8.2.0", @@ -10963,8 +10958,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "ansi-colors": { "version": "4.1.1", @@ -11948,8 +11942,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-prettier": { "version": "4.2.1", @@ -13408,8 +13401,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "28.0.2", @@ -15090,13 +15082,13 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==", - "devOptional": true + "dev": true }, "swagger-ui-express": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz", "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==", - "devOptional": true, + "dev": true, "requires": { "swagger-ui-dist": ">=4.11.0" } diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100644 index 0000000..6b02e22 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,185 @@ +#!/bin/bash +set -Eeuo pipefail +trap cleanup SIGINT SIGTERM ERR EXIT + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +usage() { + cat << EOF # remove the space between << and EOF, this is due to web plugin issue +Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-build_docker] [-build_and_run_docker] [-stop_docker] [-run_app] [-run_test] [-run_lint] [-deploy_on_kubernetes] +This script helps you to runn the application in different forms. below you can get the full list of available options. +Available options: +-h, --help Print this help and exit +-build_docker Build the docker image called "freeland:latest" +-build_and_run_docker Build the docker image and run on local machine +-stop_docker Stop running docker container named "freeland" +-run_app Run application with npm in usual way for development +-run_test Run npm test +-run_lint Run npm lint +-generate_doc Generate the code documentation +-deploy_on_kubernetes you need to have a kubernetes cluster already up and running on the machine. +EOF + exit +} +#============================================================================================================================================================================= +cleanup() { + trap - SIGINT SIGTERM ERR EXIT + # script cleanup here +} +#============================================================================================================================================================================= +setup_colors() { + if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then + NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' + else + NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' + fi +} +#============================================================================================================================================================================= +msg() { + echo >&2 -e "${1-}" +} +#============================================================================================================================================================================= +die() { + local msg=$1 + local code=${2-1} # default exit status 1 + msg "$msg" + exit "$code" +} +#============================================================================================================================================================================= +checkIfHelmIsInstalled() +{ + echo "Checking Helm ..." +if command which helm > /dev/null; then + echo "Helm is not installed! :(" + echo "Installing Helm ..." + curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 + sudo chmod 700 get_helm.sh + ./get_helm.sh + echo "Checking secrets helm plugin..." + helm plugin install https://github.com/jkroepke/helm-secrets --version v3.12.0 || true + echo "helm is installed, skipping..." + else + echo "Helm is installed :) ..." + sleep 1 +fi +} +checkIfSkaffoldIsInstalled() +{ + echo "Checking Skaffold ..." +if command which helm > /dev; then + echo "Skaffold is not installed! :(" + echo "Installing Skaffold ..." + curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \ + sudo install skaffold /usr/local/bin/ + echo "Skaffold is installed, skipping..." + else + echo "Helm is installed :) ..." + sleep 1 +fi +} +#============================================================================================================================================================================= +runOnKubernetes(){ + checkIfHelmIsInstalled + checkIfSkaffoldIsInstalled + cd .. + make +} +#============================================================================================================================================================================= +runTheApp() +{ + cd .. + npm install + npm start +} +#============================================================================================================================================================================= +runTheTests() +{ + cd .. + npm install + npm test +} +#============================================================================================================================================================================= +runtheLint() +{ + cd .. + npm install + npm run lint +} +#============================================================================================================================================================================= +runDockerImage(){ + cd .. + docker-compose up -d +} +#============================================================================================================================================================================= +stopDockerImage(){ + cd .. + docker-compose down +} +#============================================================================================================================================================================= +generateDoc(){ + cd .. + npm install + npm run doc +} +#============================================================================================================================================================================= +buildDockerImage() +{ + echo "Checking Docker ..." +if [[ $(which docker) && $(docker --version) ]]; then + echo "Docker is installed :) ..." + sleep 1 + + else + echo "Docker is not installed! :(" + echo "Installing Docker ..." + sudo apt install docker.io || true + sudo usermod -aG docker $USER + newgrp docker +fi + + echo "Checking docker-compose ..." +if [[ $(which docker-compose) && $(docker-compose --version) ]]; then + echo "docker-compose is installed :) ..." + sleep 1 + else + echo "docker-compose is not installed! :(" + echo "Installing Docker ..." + sudo apt install docker-compose || true +fi +} +init(){ + sudo chmod 666 /var/run/docker.sock +} +#============================================================================================================================================================================= +parse_params() { + # default values of variables set from params + + while :; do + case "${1-}" in + -h | --help) usage ;; + -build_docker) buildDockerImage ;; + -build_and_run_docker) runDockerImage ;; + -stop_docker) stopDockerImage ;; + -run_app) runTheApp ;; + -run_test) runTheTests ;; + -run_lint) runtheLint ;; + -generate_doc) generateDoc;; + -deploy_on_kubernetes) runOnKubernetes ;; + -v | --verbose) set -x ;; + --no-color) NO_COLOR=1 ;; + -?*) die "Unknown option: $1" ;; + *) break ;; + esac + shift + done + + args=("$@") + + return 0 +} +#============================================================================================================================================================================= +clear +setup_colors +init +parse_params "$@" +#============================================================================================================================================================================= diff --git a/src/application/controller/index.ts b/src/application/controller/index.ts index 5c0113e..030189a 100644 --- a/src/application/controller/index.ts +++ b/src/application/controller/index.ts @@ -1 +1,2 @@ export * from './health.controller' +export * from './papers.controller' \ No newline at end of file diff --git a/src/application/controller/papers.controller.ts b/src/application/controller/papers.controller.ts new file mode 100644 index 0000000..102b5d6 --- /dev/null +++ b/src/application/controller/papers.controller.ts @@ -0,0 +1,22 @@ +import { Controller, Get, Param, Put, Query, Res } from "@nestjs/common"; +import { SearchService } from "src/core/services/common/search.service"; +import { Response } from "express"; + +@Controller('papers') +export class PapersController { + constructor(private searchService: SearchService) {} + + @Get('search') + getByContext(@Query('query') query: string/*, @Query('page') page, @Query('limit') limit*/): object { + return this.searchService.findByContext(query); + } + + @Get(':id') + getByID(@Param('id') id: string): object { + if(!id) { + //response.status(400).send({msg: "fff"}); + } else { + return this.searchService.findByID(id); + } + } +} \ No newline at end of file diff --git a/src/core/services/common/search.service.ts b/src/core/services/common/search.service.ts new file mode 100644 index 0000000..bf48d60 --- /dev/null +++ b/src/core/services/common/search.service.ts @@ -0,0 +1,48 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { map, Observable } from "rxjs"; + +/** + * Search service provider + */ +@Injectable() +export class SearchService { + constructor(private readonly httpService: HttpService) {} + + // Find paper by its ID + findByID(id: string): object { + let es_query = { + query: { + query_string: { + query: 'id:' + id + } + } + } + + return this.httpService.get('http://localhost:9200/_search', { + data: es_query, + headers: {'Content-Type': 'application/json'}, + }).pipe( + map(response=>response.data) + ); + } + + // Find paper by context + findByContext(query_str: string): object { + let es_query = { + query: { + query_string: { + query: query_str, + default_field: "content" + } + } + } + + return this.httpService.get('http://localhost:9200/_search', { + data: es_query, + headers: {'Content-Type': 'application/json'}, + }).pipe( + map(response=>response.data) + ); + } +} \ No newline at end of file diff --git a/src/infrastructure/modules/app.module.ts b/src/infrastructure/modules/app.module.ts index fcb2690..90c3573 100644 --- a/src/infrastructure/modules/app.module.ts +++ b/src/infrastructure/modules/app.module.ts @@ -7,6 +7,8 @@ import { LoggerInterceptor } from '../../core/interceptors' import * as modules from '../../core/modules' import { CommonModule } from './common/common.module'; import { PrometheusModule } from '@willsoto/nestjs-prometheus'; +import { PapersController } from 'src/application'; +import { SearchModule } from './search.module'; /** * application modules list @@ -18,6 +20,7 @@ const modulesList = Object.keys(modules).map(moduleIndex => modules[moduleIndex */ @Module({ imports: [ + SearchModule, PrometheusModule.register(), CacheModule.register(), CommonModule, @@ -40,6 +43,6 @@ const modulesList = Object.keys(modules).map(moduleIndex => modules[moduleIndex useClass: LoggerInterceptor, }, ], - controllers: [], + controllers: [PapersController], }) export class AppModule {} diff --git a/src/infrastructure/modules/search.module.ts b/src/infrastructure/modules/search.module.ts new file mode 100644 index 0000000..a7c9ecf --- /dev/null +++ b/src/infrastructure/modules/search.module.ts @@ -0,0 +1,14 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { SearchService } from "src/core/services/common/search.service"; + +/** + * search module + */ +@Module({ + imports: [HttpModule], + exports: [SearchService], + providers: [SearchService], + controllers: [], +}) +export class SearchModule {} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index bd527a9..5333fdb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -37,10 +37,16 @@ async function bootstrap() { if (!NODE_PORT) { throw new Error('Please define the node port as an environmental variable'); } + + /*const ES_PORT = configService.get('ES_PORT'); + if(!ES_PORT) { + throw new Error('Please define the Elasticsearch port as an environmental variable'); + }*/ + await app.listen(NODE_PORT, () => Logger.log('HTTP Service is listening on port ' + String(NODE_PORT), 'App')); } catch (error) { console.log(error); - + return; } } diff --git a/test/search.module.spec.ts b/test/search.module.spec.ts new file mode 100644 index 0000000..c23f829 --- /dev/null +++ b/test/search.module.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { PapersController } from "../src/application/controller/papers.controller"; + +describe('PapersController', () => { + let controller: PapersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [PapersController], + }).compile(); + + controller = module.get(PapersController); + }); + + it('Should be defined', () => { + expect(controller).toBeDefined(); + }); +}); \ No newline at end of file