Merge remote-tracking branch 'origin/betterPage' into betterPage

This commit is contained in:
danny-mhlv 2022-09-08 12:21:16 +03:00
commit 5a0500e672
67 changed files with 2699 additions and 1097 deletions

View File

@ -3,8 +3,6 @@ version: '3.3'
services:
elasticsearch:
image: ${ES_IMAGE_NAME}:${ES_IMAGE_VERSION}
build:
context: .
container_name: ${ES_CONTAINER_NAME}
restart: always
ports:
@ -12,6 +10,12 @@ services:
environment:
- xpack.security.enabled=false
- discovery.type=single-node
freeland:
image: ${IMAGE_NAME}:${IMAGE_VERSION}
build:
@ -19,6 +23,8 @@ services:
dockerfile: Dockerfile
container_name: ${CONTAINER_NAME}
restart: always
links:
- "elasticsearch:localhost"
ports:
- "${LOCAL_PORT}:${NODE_PORT}"
environment:

View File

@ -92,7 +92,7 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { plainToClass } from &#x27;class-transformer&#x27;;
import { validateSync, IsOptional } from &#x27;class-validator&#x27;;
import { validateSync } from &#x27;class-validator&#x27;;
/**
* env vatiables

View File

@ -63,7 +63,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/dtos/es-hit.dto.ts</code>
<code>src/core/domain/dtos/elastic/es-hit.dto.ts</code>
</p>
@ -143,13 +143,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@ApiProperty({description: &#x27;Relevance score&#x27;, example: 1.2355})<br />
@IsOptional()<br />@ApiPropertyOptional({description: &#x27;Relevance score&#x27;, example: 1.2355})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="44" class="link-to-prism">src/core/domain/dtos/es-hit.dto.ts:44</a></div>
<div class="io-line">Defined in <a href="" data-line="45" class="link-to-prism">src/core/domain/dtos/elastic/es-hit.dto.ts:45</a></div>
</td>
</tr>
@ -190,7 +190,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="24" class="link-to-prism">src/core/domain/dtos/es-hit.dto.ts:24</a></div>
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/domain/dtos/elastic/es-hit.dto.ts:25</a></div>
</td>
</tr>
@ -226,13 +226,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@ApiProperty({description: &#x27;List of objects that represents how the hit was sorted&#x27;, example: undefined})<br />
@IsOptional()<br />@ApiPropertyOptional({description: &#x27;List of objects that represents how the hit was sorted&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="34" class="link-to-prism">src/core/domain/dtos/es-hit.dto.ts:34</a></div>
<div class="io-line">Defined in <a href="" data-line="35" class="link-to-prism">src/core/domain/dtos/elastic/es-hit.dto.ts:35</a></div>
</td>
</tr>
@ -257,9 +257,9 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsArray, IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
import { PaperDto } from &quot;./paper.dto&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from &quot;@nestjs/swagger&quot;;
import { IsNotEmpty, IsOptional } from &quot;class-validator&quot;;
import { PaperDto } from &quot;../paper.dto&quot;;
/**
* List of allowed properties in this DTO
@ -269,6 +269,7 @@ const allowedProperties &#x3D; [&#x27;sort&#x27;, &#x27;_source&#x27;, &#x27;_sc
/**
* Structure of the document stored and retrieved from Elasticsearch
*/
@ApiExtraModels()
export class EsHitDto {
/**
* Actual document stored in Elasticsearch
@ -286,7 +287,7 @@ export class EsHitDto {
* List of objects that represents how the hit was sorted
*/
@IsOptional()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;List of objects that represents how the hit was sorted&#x27;,
example: {}
})
@ -296,7 +297,7 @@ export class EsHitDto {
* Hit relevance score
*/
@IsOptional()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;Relevance score&#x27;,
example: 1.2355
})

View File

@ -63,7 +63,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/dtos/es-query.dto.ts</code>
<code>src/core/domain/dtos/elastic/es-query.dto.ts</code>
</p>
@ -133,7 +133,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="70" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:70</a></div>
<div class="io-line">Defined in <a href="" data-line="71" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:71</a></div>
</td>
</tr>
@ -175,13 +175,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsObject()<br />@ApiProperty({description: &#x27;PIT object&#x27;, example: undefined})<br />
@IsOptional()<br />@IsObject()<br />@ApiPropertyOptional({description: &#x27;PIT object&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="48" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:48</a></div>
<div class="io-line">Defined in <a href="" data-line="49" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:49</a></div>
</td>
</tr>
@ -222,7 +222,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:37</a></div>
<div class="io-line">Defined in <a href="" data-line="38" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:38</a></div>
</td>
</tr>
@ -258,13 +258,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsArray()<br />@ApiProperty({description: &#x27;&#x27;, example: undefined})<br />
@IsOptional()<br />@IsArray()<br />@ApiPropertyOptional({description: &#x27;&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="70" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:70</a></div>
<div class="io-line">Defined in <a href="" data-line="71" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:71</a></div>
</td>
</tr>
@ -300,13 +300,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsDefined()<br />@IsNumber()<br />@IsInt()<br />@ApiProperty({description: &#x27;Maximum number of elements returned by Elasticsearch&#x27;, example: 30})<br />
@IsOptional()<br />@IsDefined()<br />@IsNumber()<br />@IsInt()<br />@ApiPropertyOptional({description: &#x27;Maximum number of elements returned by Elasticsearch&#x27;, example: 30})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="26" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:26</a></div>
<div class="io-line">Defined in <a href="" data-line="27" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:27</a></div>
</td>
</tr>
@ -342,13 +342,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsArray()<br />@ApiProperty({description: &#x27;&#x27;, example: undefined})<br />
@IsOptional()<br />@IsArray()<br />@ApiPropertyOptional({description: &#x27;&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="59" class="link-to-prism">src/core/domain/dtos/es-query.dto.ts:59</a></div>
<div class="io-line">Defined in <a href="" data-line="60" class="link-to-prism">src/core/domain/dtos/elastic/es-query.dto.ts:60</a></div>
</td>
</tr>
@ -373,10 +373,10 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from &quot;@nestjs/swagger&quot;;
import { IsArray, IsDefined, IsInt, IsNotEmpty, IsNumber, IsObject, IsOptional } from &quot;class-validator&quot;;
import { EsPit } from &quot;../interfaces/es-pit.interface&quot;;
import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
import { EsPit } from &quot;../../interfaces/elastic/es-pit.interface&quot;;
import { EsQuery } from &quot;../../interfaces/elastic/es-query.interface&quot;
/**
* List of allowed properties in this DTO
@ -386,6 +386,7 @@ import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
/**
* Elasticsearch query DTO
*/
@ApiExtraModels()
export class EsQueryDto {
/**
* Maximum number of elements returned by Elasticsearch
@ -394,7 +395,7 @@ import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
@IsDefined()
@IsNumber()
@IsInt()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;Maximum number of elements returned by Elasticsearch&#x27;,
example: 30
})
@ -416,7 +417,7 @@ import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
*/
@IsOptional()
@IsObject()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;PIT object&#x27;,
example: {}
})
@ -427,7 +428,7 @@ import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
*/
@IsOptional()
@IsArray()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;&#x27;,
example: []
})
@ -438,7 +439,7 @@ import { EsQuery } from &quot;../interfaces/es-query.interface&quot;
*/
@IsOptional()
@IsArray()
@ApiProperty({
@ApiPropertyOptional({
description: &#x27;&#x27;,
example: []
})

View File

@ -63,7 +63,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/dtos/es-response.dto.ts</code>
<code>src/core/domain/dtos/elastic/es-response.dto.ts</code>
</p>
@ -147,13 +147,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsObject()<br />@ApiProperty({description: &#x27;_shards&#x27;, example: undefined})<br />
@IsOptional()<br />@IsObject()<br />@ApiProperty({description: &#x27;Contains a count of Elasticsearch shards used to process the request&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="55" class="link-to-prism">src/core/domain/dtos/es-response.dto.ts:55</a></div>
<div class="io-line">Defined in <a href="" data-line="56" class="link-to-prism">src/core/domain/dtos/elastic/es-response.dto.ts:56</a></div>
</td>
</tr>
@ -189,13 +189,13 @@ used for the request</p>
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@IsObject()<br />@ApiProperty({description: &#x27;hits&#x27;, example: undefined})<br />
@IsOptional()<br />@IsObject()<br />@ApiProperty({description: &#x27;Contains returned documents and metadata&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="83" class="link-to-prism">src/core/domain/dtos/es-response.dto.ts:83</a></div>
<div class="io-line">Defined in <a href="" data-line="80" class="link-to-prism">src/core/domain/dtos/elastic/es-response.dto.ts:80</a></div>
</td>
</tr>
@ -231,13 +231,13 @@ used for the request</p>
<b>Decorators : </b>
<br />
<code>
@IsString()<br />@IsOptional()<br />@ApiProperty({description: &#x27;PIT ID used to search for results&#x27;, example: &#x27;46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA&#x3D;&#x3D;&#x27;})<br />
@IsString()<br />@IsOptional()<br />@ApiPropertyOptional({description: &#x27;Contains PIT ID used to search for results&#x27;, example: &#x27;46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA&#x3D;&#x3D;&#x27;})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="94" class="link-to-prism">src/core/domain/dtos/es-response.dto.ts:94</a></div>
<div class="io-line">Defined in <a href="" data-line="91" class="link-to-prism">src/core/domain/dtos/elastic/es-response.dto.ts:91</a></div>
</td>
</tr>
@ -272,13 +272,13 @@ used for the request</p>
<b>Decorators : </b>
<br />
<code>
@IsDefined()<br />@IsNotEmpty()<br />@IsBoolean()<br />@ApiProperty({description: &#x27;timed_out&#x27;, example: false})<br />
@IsDefined()<br />@IsNotEmpty()<br />@IsBoolean()<br />@ApiProperty({description: &#x27;Shows if request timed out before completion&#x27;, example: false})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="38" class="link-to-prism">src/core/domain/dtos/es-response.dto.ts:38</a></div>
<div class="io-line">Defined in <a href="" data-line="39" class="link-to-prism">src/core/domain/dtos/elastic/es-response.dto.ts:39</a></div>
</td>
</tr>
@ -314,13 +314,13 @@ If &#39;true&#39; - the request timed out before completion</p>
<b>Decorators : </b>
<br />
<code>
@IsDefined()<br />@IsNotEmpty()<br />@IsNumber()<br />@ApiProperty({description: &#x27;took&#x27;, example: 5})<br />
@IsDefined()<br />@IsNotEmpty()<br />@IsNumber()<br />@ApiProperty({description: &#x27;The time that it took Elasticsearch to process the query&#x27;, example: 5})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/domain/dtos/es-response.dto.ts:25</a></div>
<div class="io-line">Defined in <a href="" data-line="26" class="link-to-prism">src/core/domain/dtos/elastic/es-response.dto.ts:26</a></div>
</td>
</tr>
@ -346,9 +346,9 @@ took Elasticsearch to execute the request</p>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from &quot;@nestjs/swagger&quot;;
import { IsBoolean, IsDefined, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from &quot;class-validator&quot;;
import { EsResponseHits } from &quot;../interfaces/es-response-hits.interface&quot;;
import { EsResponseHits } from &quot;../../interfaces/elastic/es-response-hits.interface&quot;;
/**
* List of allowed properties in this DTO
@ -358,6 +358,7 @@ const allowedProperties &#x3D; [&#x27;took&#x27;, &#x27;timed_out&#x27;, &#x27;_
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class EsResponseDto {
/**
* Number of milliseconds it
@ -367,7 +368,7 @@ export class EsResponseDto {
@IsNotEmpty()
@IsNumber()
@ApiProperty({
description: &#x27;took&#x27;,
description: &#x27;The time that it took Elasticsearch to process the query&#x27;,
example: 5
})
took: number;
@ -380,7 +381,7 @@ export class EsResponseDto {
@IsNotEmpty()
@IsBoolean()
@ApiProperty({
description: &#x27;timed_out&#x27;,
description: &#x27;Shows if request timed out before completion&#x27;,
example: false,
})
timed_out: boolean;
@ -392,7 +393,7 @@ export class EsResponseDto {
@IsOptional()
@IsObject()
@ApiProperty({
description: &#x27;_shards&#x27;,
description: &#x27;Contains a count of Elasticsearch shards used to process the request&#x27;,
example: {
total: 1,
successful: 1,
@ -408,7 +409,7 @@ export class EsResponseDto {
@IsOptional()
@IsObject()
@ApiProperty({
description: &#x27;hits&#x27;,
description: &#x27;Contains returned documents and metadata&#x27;,
example: {
total: {
value: 3,
@ -419,12 +420,8 @@ export class EsResponseDto {
_index: &#x27;papers&#x27;,
_id: &#x27;01002&#x27;,
_score: 1.2,
_source: {
},
fields: {
}
_source: {},
fields: {}
}],
}
})
@ -435,8 +432,8 @@ export class EsResponseDto {
*/
@IsString()
@IsOptional()
@ApiProperty({
description: &#x27;PIT ID used to search for results&#x27;,
@ApiPropertyOptional({
description: &#x27;Contains PIT ID used to search for results&#x27;,
example: &#x27;46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA&#x3D;&#x3D;&#x27;
})
pit_id?: string;

View File

@ -122,7 +122,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="32" class="link-to-prism">src/core/domain/dtos/page.dto.ts:32</a></div>
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/page.dto.ts:37</a></div>
</td>
</tr>
@ -202,13 +202,13 @@
<b>Decorators : </b>
<br />
<code>
@IsArray()<br />@ApiProperty({description: &#x27;All data the page contains&#x27;, isArray: true})<br />
@IsArray()<br />@ApiProperty({description: &#x27;All data (papers) the page contains&#x27;, isArray: true, type: PaperDto})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="23" class="link-to-prism">src/core/domain/dtos/page.dto.ts:23</a></div>
<div class="io-line">Defined in <a href="" data-line="27" class="link-to-prism">src/core/domain/dtos/page.dto.ts:27</a></div>
</td>
</tr>
@ -235,7 +235,7 @@
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../interfaces/PageMeta.html" target="_self" >PageMeta</a></code>
<i>Type : </i> <code><a href="../interfaces/PageMeta.html" target="_self" >PageMetaDto</a></code>
</td>
</tr>
@ -250,7 +250,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="32" class="link-to-prism">src/core/domain/dtos/page.dto.ts:32</a></div>
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/page.dto.ts:37</a></div>
</td>
</tr>
@ -275,9 +275,11 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, PartialType } from &quot;@nestjs/swagger&quot;;
import { IsArray } from &quot;class-validator&quot;;
import { Order } from &quot;../enums&quot;;
import { PageMeta } from &quot;../interfaces/page-meta.interface&quot;;
import { PageMetaDto } from &quot;./page-meta.dto&quot;;
import { PaperDto } from &quot;./paper.dto&quot;;
/**
@ -288,14 +290,16 @@ const allowedProperties &#x3D; [&#x27;data&#x27;, &#x27;meta&#x27;];
/**
* Page model for pagination
*/
@ApiExtraModels()
export class PageDto {
/**
* Data block of the page
*/
@IsArray()
@ApiProperty({
description: &#x27;All data the page contains&#x27;,
description: &#x27;All data (papers) the page contains&#x27;,
isArray: true,
type: PaperDto
})
readonly data: PaperDto[];
@ -304,9 +308,10 @@ export class PageDto {
*/
@ApiProperty({
description: &#x27;Metadata for the page&#x27;,
// example: [],
// example: {},
})
readonly meta: PageMeta;
readonly meta: PageMetaDto;
/**
* Constructs an object with provided parameters

View File

@ -0,0 +1,536 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>hometask documentation</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="../images/favicon.ico">
<link rel="stylesheet" href="../styles/style.css">
<link rel="stylesheet" href="../styles/dark.css">
<link rel="stylesheet" href="../styles/readthedocs.css">
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top visible-xs">
<a href="../" class="navbar-brand">hometask documentation</a>
<button type="button" class="btn btn-default btn-menu ion-ios-menu" id="btn-menu"></button>
</div>
<div class="xs-menu menu" id="mobile-menu">
<div id="book-search-input" role="search"><input type="text" placeholder="Type to search"></div> <compodoc-menu></compodoc-menu>
</div>
<div class="container-fluid main">
<div class="row main">
<div class="hidden-xs menu">
<compodoc-menu mode="normal"></compodoc-menu>
</div>
<!-- START CONTENT -->
<div class="content class">
<div class="content-data">
<ol class="breadcrumb">
<li>Classes</li>
<li >PageMetaDto</li>
</ol>
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a href="#info" role="tab" id="info-tab" data-toggle="tab" data-link="info">Info</a>
</li>
<li >
<a href="#source" role="tab" id="source-tab" data-toggle="tab" data-link="source">Source</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade active in" id="c-info">
<p class="comment">
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/dtos/page-meta.dto.ts</code>
</p>
<p class="comment">
<h3>Description</h3>
</p>
<p class="comment">
<p>Page model for pagination</p>
</p>
<p class="comment">
<h3>Implements</h3>
</p>
<p class="comment">
<code><a href="../interfaces/PageMeta.html" target="_self" >PageMeta</a></code>
</p>
<section>
<h3 id="index">Index</h3>
<table class="table table-sm table-bordered index-table">
<tbody>
<tr>
<td class="col-md-4">
<h6><b>Properties</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<a href="#hasNext" >hasNext</a>
</li>
<li>
<a href="#hasPrev" >hasPrev</a>
</li>
<li>
<a href="#order" >order</a>
</li>
<li>
<a href="#pagenum" >pagenum</a>
</li>
<li>
<a href="#pagesize" >pagesize</a>
</li>
<li>
<a href="#total" >total</a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</section>
<section>
<h3 id="inputs">
Properties
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="hasNext"></a>
<span class="name">
<span ><b>hasNext</b></span>
<a href="#hasNext"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/boolean" target="_blank" >boolean</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@ApiProperty({description: &#x27;Flag, that shows if there&#x27;s a page following the current one&#x27;, example: true})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="53" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:53</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Flag, that shows if there&#39;s a page following the current one</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="hasPrev"></a>
<span class="name">
<span ><b>hasPrev</b></span>
<a href="#hasPrev"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/boolean" target="_blank" >boolean</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@ApiProperty({description: &#x27;Flag, that shows if there&#x27;s a page preceding the current one&#x27;, example: true})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="62" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:62</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Flag, that shows if there&#39;s a page preceding the current one</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="order"></a>
<span class="name">
<span ><b>order</b></span>
<a href="#order"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/enumerations.html#Order" target="_self" >Order</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@ApiProperty({description: &#x27;Order of the elements on the page&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="44" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:44</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Order of the elements on the page</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="pagenum"></a>
<span class="name">
<span ><b>pagenum</b></span>
<a href="#pagenum"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/number" target="_blank" >number</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@ApiProperty({description: &#x27;Current page number&#x27;, minimum: 1, example: 3})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="35" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:35</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Current page number</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="pagesize"></a>
<span class="name">
<span ><b>pagesize</b></span>
<a href="#pagesize"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/number" target="_blank" >number</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@ApiProperty({description: &#x27;Maximum number of elements on the page&#x27;, minimum: 1, example: 20})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="72" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:72</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Maximum number of elements on the page</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="total"></a>
<span class="name">
<span ><b>total</b></span>
<a href="#total"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/number" target="_blank" >number</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>
@IsArray()<br />@ApiProperty({description: &#x27;Total number of hits (results) acquired from the search&#x27;, example: 314})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/domain/dtos/page-meta.dto.ts:25</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Total number of hits (results) acquired from the search</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, PartialType } from &quot;@nestjs/swagger&quot;;
import { IsArray } from &quot;class-validator&quot;;
import { Order } from &quot;../enums&quot;;
import { PageMeta } from &quot;../interfaces/page-meta.interface&quot;;
import { PaperDto } from &quot;./paper.dto&quot;;
/**
* List of allowed properties in this DTO
*/
const allowedProperties &#x3D; [&#x27;total&#x27;, &#x27;pagenum&#x27;, &#x27;order&#x27;, &#x27;hasNext&#x27;, &#x27;hasPrev&#x27;, &#x27;pagesize&#x27;];
/**
* Page model for pagination
*/
@ApiExtraModels()
export class PageMetaDto implements PageMeta {
/**
* Total number of hits (results) acquired from the search
*/
@IsArray()
@ApiProperty({
description: &#x27;Total number of hits (results) acquired from the search&#x27;,
example: 314
})
total: number;
/**
* Current page number
*/
@ApiProperty({
description: &#x27;Current page number&#x27;,
minimum: 1,
example: 3
})
pagenum: number;
/**
* Order of the elements on the page
*/
@ApiProperty({
description: &#x27;Order of the elements on the page&#x27;,
example: Order.DESC
})
order: Order;
/**
* Flag, that shows if there&#x27;s a page following the current one
*/
@ApiProperty({
description: &#x27;Flag, that shows if there\&#x27;s a page following the current one&#x27;,
example: true
})
hasNext: boolean;
/**
* Flag, that shows if there&#x27;s a page preceding the current one
*/
@ApiProperty({
description: &#x27;Flag, that shows if there\&#x27;s a page preceding the current one&#x27;,
example: true
})
hasPrev: boolean;
/**
* Maximum number of elements on the page
*/
@ApiProperty({
description: &#x27;Maximum number of elements on the page&#x27;,
minimum: 1,
example: 20
})
pagesize: number;
}</code></pre>
</div>
</div>
</div><div class="search-results">
<div class="has-results">
<h1 class="search-results-title"><span class='search-results-count'></span> results matching "<span class='search-query'></span>"</h1>
<ul class="search-results-list"></ul>
</div>
<div class="no-results">
<h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1>
</div>
</div>
</div>
<!-- END CONTENT -->
</div>
</div>
<label class="dark-mode-switch">
<input type="checkbox">
<span class="slider">
<svg class="slider-icon" viewBox="0 0 24 24" fill="none" height="20" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="20" xmlns="http://www.w3.org/2000/svg">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"></path>
</svg>
</span>
</label>
<script>
var COMPODOC_CURRENT_PAGE_DEPTH = 1;
var COMPODOC_CURRENT_PAGE_CONTEXT = 'class';
var COMPODOC_CURRENT_PAGE_URL = 'PageMetaDto.html';
var MAX_SEARCH_RESULTS = 15;
</script>
<script src="../js/libs/custom-elements.min.js"></script>
<script src="../js/libs/lit-html.js"></script>
<script src="../js/menu-wc.js" defer></script>
<script nomodule src="../js/menu-wc_es5.js" defer></script>
<script src="../js/libs/bootstrap-native.js"></script>
<script src="../js/libs/es6-shim.min.js"></script>
<script src="../js/libs/EventDispatcher.js"></script>
<script src="../js/libs/promise.min.js"></script>
<script src="../js/libs/zepto.min.js"></script>
<script src="../js/compodoc.js"></script>
<script src="../js/tabs.js"></script>
<script src="../js/menu.js"></script>
<script src="../js/libs/clipboard.min.js"></script>
<script src="../js/libs/prism.js"></script>
<script src="../js/sourceCode.js"></script>
<script src="../js/search/search.js"></script>
<script src="../js/search/lunr.min.js"></script>
<script src="../js/search/search-lunr.js"></script>
<script src="../js/search/search_index.js"></script>
<script src="../js/lazy-load-graphs.js"></script>
</body>
</html>

View File

@ -158,7 +158,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="46" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:46</a></div>
<div class="io-line">Defined in <a href="" data-line="45" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:45</a></div>
</td>
</tr>
@ -199,7 +199,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="88" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:88</a></div>
<div class="io-line">Defined in <a href="" data-line="87" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:87</a></div>
</td>
</tr>
@ -240,7 +240,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="24" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:24</a></div>
<div class="io-line">Defined in <a href="" data-line="23" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:23</a></div>
</td>
</tr>
@ -281,7 +281,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="68" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:68</a></div>
<div class="io-line">Defined in <a href="" data-line="67" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:67</a></div>
</td>
</tr>
@ -322,7 +322,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="79" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:79</a></div>
<div class="io-line">Defined in <a href="" data-line="78" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:78</a></div>
</td>
</tr>
@ -363,7 +363,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="35" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:35</a></div>
<div class="io-line">Defined in <a href="" data-line="34" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:34</a></div>
</td>
</tr>
@ -404,7 +404,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="57" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:57</a></div>
<div class="io-line">Defined in <a href="" data-line="56" class="link-to-prism">src/core/domain/dtos/paper.dto.ts:56</a></div>
</td>
</tr>
@ -429,10 +429,8 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsArray, IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
import { EsQueryDto } from &quot;./es-query.dto&quot;;
import { SearchQueryDto } from &quot;./search-q.dto&quot;;
/**
* List of allowed properties in this DTO
@ -442,6 +440,7 @@ const allowedProperties &#x3D; [&#x27;id&#x27;, &#x27;title&#x27;, &#x27;authors
/**
* Structure of the document stored and retrieved from Elasticsearch
*/
@ApiExtraModels()
export class PaperDto {
/**
* Unique ID of the paper

View File

@ -76,12 +76,6 @@
</p>
<p class="comment">
<h3>Implements</h3>
</p>
<p class="comment">
<code><a href="../interfaces/SearchInfo.html" target="_self" >SearchInfo</a></code>
</p>
<section>
@ -97,12 +91,15 @@
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Private</span>
<a href="#pit" >pit</a>
</li>
<li>
<span class="modifier">Private</span>
<a href="#prevPage" >prevPage</a>
</li>
<li>
<span class="modifier">Private</span>
<a href="#tiebreaker" >tiebreaker</a>
</li>
</ul>
@ -129,6 +126,26 @@
<tr>
<td class="col-md-4">
<h6><b>Accessors</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<a href="#_pit" >_pit</a>
</li>
<li>
<a href="#_tiebreaker" >_tiebreaker</a>
</li>
<li>
<a href="#_prevPage" >_prevPage</a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</section>
@ -144,7 +161,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="20" class="link-to-prism">src/core/interceptors/page.interceptor.ts:20</a></div>
<div class="io-line">Defined in <a href="" data-line="16" class="link-to-prism">src/core/interceptors/page.interceptor.ts:16</a></div>
</td>
</tr>
@ -169,6 +186,7 @@
<td class="col-md-4">
<a name="pit"></a>
<span class="name">
<span class="modifier">Private</span>
<span ><b>pit</b></span>
<a href="#pit"><span class="icon ion-ios-link"></span></a>
</span>
@ -182,7 +200,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="33" class="link-to-prism">src/core/interceptors/page.interceptor.ts:33</a></div>
<div class="io-line">Defined in <a href="" data-line="29" class="link-to-prism">src/core/interceptors/page.interceptor.ts:29</a></div>
</td>
</tr>
@ -201,6 +219,7 @@
<td class="col-md-4">
<a name="prevPage"></a>
<span class="name">
<span class="modifier">Private</span>
<span ><b>prevPage</b></span>
<a href="#prevPage"><span class="icon ion-ios-link"></span></a>
</span>
@ -214,7 +233,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="43" class="link-to-prism">src/core/interceptors/page.interceptor.ts:43</a></div>
<div class="io-line">Defined in <a href="" data-line="51" class="link-to-prism">src/core/interceptors/page.interceptor.ts:51</a></div>
</td>
</tr>
@ -233,6 +252,7 @@
<td class="col-md-4">
<a name="tiebreaker"></a>
<span class="name">
<span class="modifier">Private</span>
<span ><b>tiebreaker</b></span>
<a href="#tiebreaker"><span class="icon ion-ios-link"></span></a>
</span>
@ -246,7 +266,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="38" class="link-to-prism">src/core/interceptors/page.interceptor.ts:38</a></div>
<div class="io-line">Defined in <a href="" data-line="40" class="link-to-prism">src/core/interceptors/page.interceptor.ts:40</a></div>
</td>
</tr>
@ -288,8 +308,8 @@
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="49"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:49</a></div>
<div class="io-line">Defined in <a href="" data-line="63"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:63</a></div>
</td>
</tr>
@ -319,30 +339,238 @@
<section>
<h3 id="accessors">
Accessors
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="_pit"></a>
<span class="name"><b>_pit</b><a href="#_pit"><span class="icon ion-ios-link"></span></a></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>get</b><code>_pit()</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="33" class="link-to-prism">src/core/interceptors/page.interceptor.ts:33</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>set</b><code>_pit(pit: <a href="../interfaces/EsPit.html" target="_self">EsPit</a>)</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="30" class="link-to-prism">src/core/interceptors/page.interceptor.ts:30</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description">
<b>Parameters :</b>
<table class="params">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Optional</td>
</tr>
</thead>
<tbody>
<tr>
<td>pit</td>
<td>
<code><a href="../interfaces/EsPit.html" target="_self" >EsPit</a></code>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</div>
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="https://www.typescriptlang.org/docs/handbook/basic-types.html" target="_blank" >void</a></code>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="_tiebreaker"></a>
<span class="name"><b>_tiebreaker</b><a href="#_tiebreaker"><span class="icon ion-ios-link"></span></a></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>get</b><code>_tiebreaker()</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="44" class="link-to-prism">src/core/interceptors/page.interceptor.ts:44</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>set</b><code>_tiebreaker(tiebreaker: [])</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="41" class="link-to-prism">src/core/interceptors/page.interceptor.ts:41</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description">
<b>Parameters :</b>
<table class="params">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Optional</td>
</tr>
</thead>
<tbody>
<tr>
<td>tiebreaker</td>
<td>
<code>[]</code>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</div>
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="https://www.typescriptlang.org/docs/handbook/basic-types.html" target="_blank" >void</a></code>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="_prevPage"></a>
<span class="name"><b>_prevPage</b><a href="#_prevPage"><span class="icon ion-ios-link"></span></a></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>get</b><code>_prevPage()</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="55" class="link-to-prism">src/core/interceptors/page.interceptor.ts:55</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="accessor"><b>set</b><code>_prevPage(page: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/number" target="_blank">number</a>)</code></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="52" class="link-to-prism">src/core/interceptors/page.interceptor.ts:52</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description">
<b>Parameters :</b>
<table class="params">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Optional</td>
</tr>
</thead>
<tbody>
<tr>
<td>page</td>
<td>
<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/number" target="_blank" >number</a></code>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</div>
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="https://www.typescriptlang.org/docs/handbook/basic-types.html" target="_blank" >void</a></code>
</div>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { HttpService } from &quot;@nestjs/axios&quot;;
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from &quot;@nestjs/common&quot;;
import { reverse } from &quot;dns&quot;;
import { Observable, map, take } from &quot;rxjs&quot;;
import { EsResponseDto, PageDto } from &quot;../domain/dtos&quot;;
import { EsQueryDto } from &quot;../domain/dtos/es-query.dto&quot;;
import { PageDto } from &quot;../domain/dtos&quot;;
import { EsQueryDto } from &quot;../domain/dtos/elastic/es-query.dto&quot;;
import { RequestDto } from &quot;../domain/dtos/request.dto&quot;;
import { SearchQueryDto } from &quot;../domain/dtos/search-q.dto&quot;;
import { SearchResultDto } from &quot;../domain/dtos/search-result.dto&quot;;
import { EsTime } from &quot;../domain/enums/es-time.enum&quot;;
import { Order } from &quot;../domain/enums/page-order.enum&quot;;
import { PageMeta } from &quot;../domain/interfaces&quot;;
import { EsPit } from &quot;../domain/interfaces/es-pit.interface&quot;;
import { SearchInfo } from &quot;../domain/interfaces/search-info.interface&quot;;
import { SearchService } from &quot;../services/common/search.service&quot;;
import { EsPit } from &quot;../domain/interfaces/elastic/es-pit.interface&quot;;
/**
* Previous search data storage
*/
class PrevSearch implements SearchInfo {
class PrevSearch {
/**
* Constructs an uninitialized object
*/
@ -355,17 +583,35 @@ class PrevSearch implements SearchInfo {
/**
* PIT object of the previous search
*/
pit: EsPit;
private pit: EsPit;
set _pit(pit: EsPit) {
this.pit &#x3D; pit;
}
get _pit(): EsPit {
return this.pit;
}
/**
* Tiebreaker and sort parameters
*/
tiebreaker: unknown[];
private tiebreaker: unknown[];
set _tiebreaker(tiebreaker: unknown[]) {
this.tiebreaker &#x3D; tiebreaker;
}
get _tiebreaker(): unknown[] {
return this.tiebreaker;
}
/**
* Number of the previous page
*/
prevPage: number;
private prevPage: number;
set _prevPage(page: number) {
this.prevPage &#x3D; page;
}
get _prevPage(): number {
return this.prevPage;
}
/**
* Checks if there was the search before current one
@ -416,22 +662,26 @@ export class PageInterceptor implements NestInterceptor {
];
if (this.prevSearch.isSet()) {
request.es_query.pit &#x3D; this.prevSearch.pit;
request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
request.es_query.pit &#x3D; this.prevSearch._pit;
request.es_query.search_after &#x3D; this.prevSearch._tiebreaker;
let limit &#x3D; !query?.limit ? 10 : query.limit;
request.es_query.size &#x3D; limit * Math.abs(query.page - this.prevSearch.prevPage);
request.es_query.size &#x3D; limit * Math.abs(query.page - this.prevSearch._prevPage);
if (query.page &lt; this.prevSearch.prevPage) {
if (query.page &lt; this.prevSearch._prevPage) {
request.es_query.sort &#x3D; [{ _score: { order: &#x27;asc&#x27; } }];
request.es_query.size +&#x3D; limit - 1;
reverse &#x3D; true;
} else if (query.page &#x3D;&#x3D; this.prevSearch.prevPage) {
//...
} else if (query.page &#x3D;&#x3D; this.prevSearch._prevPage) {
// Caching should be HERE
request.es_query.sort &#x3D; [{ _score: { order: &#x27;asc&#x27; } }];
reverse &#x3D; true;
}
} else {
this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
request.es_query.size &#x3D; !query?.limit ? 10 : query.limit;
this.prevSearch._pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
let limit &#x3D; !query?.limit ? 10 : query.limit;
request.es_query.size &#x3D; limit * query.page;
}
return next.handle().pipe(
@ -439,28 +689,31 @@ export class PageInterceptor implements NestInterceptor {
// Setting the page meta-data
let meta: PageMeta &#x3D; {
total: res.hits.total.value,
pagenum: !query?.page ? 1 : query.page,
pagenum: !query?.page ? 1 : +query.page,
order: query?.order?.toUpperCase() &#x3D;&#x3D;&#x3D; Order.ASC ? Order.ASC : Order.DESC,
hasNext: false,
hasPrev: false,
pagesize: !query?.limit ? 10 : query.limit,
hasNext: undefined,
hasPrev: undefined,
};
// meta.hasNext &#x3D; res.hits.hits[meta.pagenum * meta.pagesize] ? true : false;
// meta.hasPrev &#x3D; res.hits.hits[(meta.pagenum - 1) * meta.pagesize - 1] ? true: false;
meta.hasNext &#x3D; meta.pagenum * meta.pagesize &lt; meta.total ? true : false;
meta.hasPrev &#x3D; meta.pagenum !&#x3D; 1 ? true : false;
// Saving the search info
this.prevSearch.pit.id &#x3D; res.pit_id;
this.prevSearch.tiebreaker &#x3D; res.hits.hits[res.hits.hits.length - 1].sort;
this.prevSearch.prevPage &#x3D; query.page;
this.prevSearch._pit.id &#x3D; res.pit_id;
this.prevSearch._tiebreaker &#x3D; res.hits.hits[res.hits.hits.length - 1]?.sort;
this.prevSearch._prevPage &#x3D; query.page;
// Check if the performed search is a backwards search
let data &#x3D; res.hits.hits.slice(-meta.pagesize);
if (reverse) {
console.log(&#x27;REVERSE&#x27;);
this.prevSearch.tiebreaker &#x3D; data[0].sort;
this.prevSearch._tiebreaker &#x3D; data[0]?.sort;
data.reverse();
reverse &#x3D; false;
}
// Omitting the redundant info and leaving only the document
data &#x3D; data.map((el) &#x3D;&gt; el._source);
// Return the page
return new PageDto(data, meta);
})
@ -472,6 +725,11 @@ export class PageInterceptor implements NestInterceptor {
*/
private readonly ES_PORT &#x3D; process.env.ES_PORT;
/**
* Elastichsearch IP address
*/
private readonly ES_IP &#x3D; process.env.ES_CONTAINER_NAME;
/**
* Info about previously completed search
*/
@ -485,12 +743,12 @@ export class PageInterceptor implements NestInterceptor {
public async getPIT(alive: number, unit: EsTime &#x3D; EsTime.min): Promise&lt;EsPit&gt; {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
(this.httpService.post&lt;EsPit&gt;(&#x60;http://localhost:${this.ES_PORT}/papers/_pit?keep_alive&#x3D;${alive+unit}&#x60;)
this.httpService.post&lt;EsPit&gt;(&#x60;http://${this.ES_IP}:${this.ES_PORT}/papers/_pit?keep_alive&#x3D;${alive+unit}&#x60;)
.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
.subscribe((res) &#x3D;&gt; {
.subscribe((res: EsPit) &#x3D;&gt; {
res.keep_alive &#x3D; alive + unit;
resolve(res);
}));
});
} catch (error) {
reject(error);
}
@ -505,7 +763,7 @@ export class PageInterceptor implements NestInterceptor {
async deletePIT(pitID: string): Promise&lt;boolean&gt; {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
this.httpService.delete(&#x60;http://localhost:${this.ES_PORT}/_pit&#x60;, {
this.httpService.delete(&#x60;http://${this.ES_IP}:${this.ES_PORT}/_pit&#x60;, {
data: { id: pitID },
headers: { &#x27;Content-Type&#x27;: &#x27;application/json&#x27; },
})
@ -518,72 +776,7 @@ export class PageInterceptor implements NestInterceptor {
}
})
}
}
/*
public saveInfo(pit: EsPit, tiebreaker: unknown[], page: number) {
this.pit.id &#x3D; pit.id;
this.pit.keep_alive &#x3D; pit.keep_alive;
this.tiebreaker &#x3D; tiebreaker.slice();
this.prevPage &#x3D; page;
}
public clearInfo() {
this.pit &#x3D; undefined;
this.tiebreaker &#x3D; undefined;
this.prevPage &#x3D; -1;
}*/
// getQueryParams(str: string): any {
// let parameters: object &#x3D; {};
// let pairs: string[] &#x3D; str.split(&#x27;,&#x27;);
// parameters[&#x27;main&#x27;] &#x3D; pairs[0];
// pairs.shift();
// if(!pairs || pairs[0] &#x3D;&#x3D;&#x3D; &#x27;&#x27;) return parameters;
// for (const pair of pairs) {
// const key: string &#x3D; pair.substring(0, pair.indexOf(&#x27;&#x3D;&#x27;));
// const value: string &#x3D; pair.substring(pair.indexOf(&#x27;&#x3D;&#x27;) + 1);
// parameters[key] &#x3D; value;
// }
// return parameters;
// }
/**
* OLD WAY PAGINATION
* // Setting the page data
// const data &#x3D; res.hits.slice((meta.pagenum - 1) * meta.pagesize, meta.pagenum * meta.pagesize);
*/
// if (query.page &#x3D;&#x3D; 1) {
// this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
// } else {
// if (!this.prevSearch.isSet()) {
// this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
// request.es_query.size &#x3D; query.limit * (query.page - 1);
// this.searchService.findByContext(request.es_query).then((res: SearchResultDto) &#x3D;&gt; {
// request.es_query.search_after &#x3D; res.data.hits.hits[res.data.hits.hits.length - 1].sort;
// });
// } else {
// if (query.page &#x3D;&#x3D; this.prevSearch.prevPage) {
// return;
// } else {
// request.es_query.pit &#x3D; this.prevSearch.pit;
// request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
// request.es_query.size &#x3D; (query.page - this.prevSearch.prevPage);
// }
// // request.es_query.pit &#x3D; this.prevSearch.pit;
// // request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
// }
// }
</code></pre>
}</code></pre>
</div>
</div>

View File

@ -121,7 +121,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="34" class="link-to-prism">src/core/domain/dtos/request.dto.ts:34</a></div>
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/request.dto.ts:37</a></div>
</td>
</tr>
@ -201,13 +201,13 @@
<b>Decorators : </b>
<br />
<code>
@IsOptional()<br />@ApiProperty({description: &#x27;&#x27;, example: undefined})<br />
@IsOptional()<br />@ApiPropertyOptional({type: EsQueryDto, description: &#x27;Elasticsearch query body constructed by pagination mechanism&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="34" class="link-to-prism">src/core/domain/dtos/request.dto.ts:34</a></div>
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/request.dto.ts:37</a></div>
</td>
</tr>
@ -242,13 +242,13 @@
<b>Decorators : </b>
<br />
<code>
@IsDefined()<br />@IsNotEmpty()<br />@ApiProperty({description: &#x27;&#x27;, example: undefined})<br />
@IsDefined()<br />@IsNotEmpty()<br />@ApiProperty({type: SearchQueryDto, description: &#x27;Actual query with parameters acquired from the request&#x27;, example: undefined})<br />
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="24" class="link-to-prism">src/core/domain/dtos/request.dto.ts:24</a></div>
<div class="io-line">Defined in <a href="" data-line="26" class="link-to-prism">src/core/domain/dtos/request.dto.ts:26</a></div>
</td>
</tr>
@ -273,9 +273,9 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
import { EsQueryDto } from &quot;./es-query.dto&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from &quot;@nestjs/swagger&quot;;
import { IsDefined, IsNotEmpty, IsOptional } from &quot;class-validator&quot;;
import { EsQueryDto } from &quot;./elastic/es-query.dto&quot;;
import { SearchQueryDto } from &quot;./search-q.dto&quot;;
/**
@ -286,6 +286,7 @@ const allowedProperties &#x3D; [&#x27;query&#x27;, &#x27;es_query&#x27;];
/**
* Request object, which contains query parameters and Elasticsearch query object
*/
@ApiExtraModels()
export class RequestDto {
/**
* Query parameters object
@ -293,7 +294,8 @@ export class RequestDto {
@IsDefined()
@IsNotEmpty()
@ApiProperty({
description: &#x27;&#x27;,
type: SearchQueryDto,
description: &#x27;Actual query with parameters acquired from the request&#x27;,
example: {}
})
query: SearchQueryDto;
@ -302,8 +304,9 @@ export class RequestDto {
* Elasticsearch query object
*/
@IsOptional()
@ApiProperty({
description: &#x27;&#x27;,
@ApiPropertyOptional({
type: EsQueryDto,
description: &#x27;Elasticsearch query body constructed by pagination mechanism&#x27;,
example: {},
})
es_query?: EsQueryDto;
@ -313,10 +316,10 @@ export class RequestDto {
* @param query
* @param es_query
*/
constructor(query: SearchQueryDto, es_query: EsQueryDto) {
constructor(query: SearchQueryDto, es_query: EsQueryDto) {
this.query &#x3D; query;
this.es_query &#x3D; es_query;
}
}
}</code></pre>
</div>
</div>

View File

@ -126,7 +126,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="58" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:58</a></div>
<div class="io-line">Defined in <a href="" data-line="59" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:59</a></div>
</td>
</tr>
@ -235,7 +235,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="47" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:47</a></div>
<div class="io-line">Defined in <a href="" data-line="48" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:48</a></div>
</td>
</tr>
@ -276,7 +276,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="58" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:58</a></div>
<div class="io-line">Defined in <a href="" data-line="59" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:59</a></div>
</td>
</tr>
@ -317,7 +317,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="36" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:36</a></div>
<div class="io-line">Defined in <a href="" data-line="37" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:37</a></div>
</td>
</tr>
@ -358,7 +358,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="24" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:24</a></div>
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/domain/dtos/search-q.dto.ts:25</a></div>
</td>
</tr>
@ -384,8 +384,8 @@ search on.</p>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsDefined, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
/**
* List of allowed properties in this DTO
@ -395,6 +395,7 @@ const allowedProperties &#x3D; [&#x27;query&#x27;, &#x27;pagen&#x27;, &#x27;limi
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class SearchQueryDto {
/**
* Given query string to perform the

View File

@ -120,7 +120,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="38" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:38</a></div>
<div class="io-line">Defined in <a href="" data-line="42" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:42</a></div>
</td>
</tr>
@ -205,7 +205,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="38" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:38</a></div>
<div class="io-line">Defined in <a href="" data-line="42" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:42</a></div>
</td>
</tr>
@ -246,7 +246,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="24" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:24</a></div>
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/domain/dtos/search-result.dto.ts:25</a></div>
</td>
</tr>
@ -271,9 +271,9 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsArray, IsDefined, IsInt, IsNotEmpty, IsOptional, IsString } from &quot;class-validator&quot;;
import { EsResponseDto } from &quot;./es-response.dto&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { ApiExtraModels, ApiProperty } from &quot;@nestjs/swagger&quot;;
import { IsArray, IsDefined, IsInt, IsNotEmpty } from &quot;class-validator&quot;;
import { EsResponseDto } from &quot;./elastic/es-response.dto&quot;;
/**
* List of allowed properties in this DTO
@ -283,6 +283,7 @@ const allowedProperties &#x3D; [&#x27;data&#x27;, &#x27;status&#x27;];
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class SearchResultDto {
/**
* Status code
@ -305,7 +306,10 @@ export class SearchResultDto {
@ApiProperty({
description: &#x27;Data acquired from the Elasticsearch&#x27;,
example: {
took: 1,
timed_out: false,
_shards: {},
hits: {}
},
})
data: EsResponseDto;

View File

@ -59,12 +59,6 @@
<code>src/application/controller/papers.controller.ts</code>
</p>
<p class="comment">
<h3>Prefix</h3>
</p>
<p class="comment">
<code>papers</code>
</p>
<p class="comment">
@ -127,7 +121,7 @@
</tr>
<tr>
<td class="col-md-4">
<code>getByContext(query: <a href="../classes/RequestDto.html" target="_self">RequestDto</a>)</code>
<code>getByContext(request: <a href="../classes/RequestDto.html" target="_self">RequestDto</a>)</code>
</td>
</tr>
@ -135,14 +129,14 @@
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>@ApiOperation({summary: &#x27;Finds papers by context based on the query.&#x27;})<br />@ApiResponse({status: 200, description: &#x27;Returns back acquired papers.&#x27;, type: SearchResultDto})<br />@Get(&#x27;search&#x27;)<br />@UseInterceptors(PageInterceptor)<br />@HttpCode(200)<br /></code>
<code>@ApiTags(&#x27;Search&#x27;)<br />@ApiOperation({summary: &#x27;Finds papers by context based on the query&#x27;})<br />@ApiResponse({status: 200, description: &#x27;Returns back a page with acquired papers&#x27;, type: PageDto})<br />@ApiGatewayTimeoutResponse({description: &#x27;Elasticsearch request timed out&#x27;})<br />@Get(&#x27;search&#x27;)<br />@UseInterceptors(PageInterceptor)<br />@HttpCode(200)<br /></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="30"
class="link-to-prism">src/application/controller/papers.controller.ts:30</a></div>
<div class="io-line">Defined in <a href="" data-line="40"
class="link-to-prism">src/application/controller/papers.controller.ts:40</a></div>
</td>
</tr>
@ -165,7 +159,7 @@
</thead>
<tbody>
<tr>
<td>query</td>
<td>request</td>
<td>
<code><a href="../classes/RequestDto.html" target="_self" >RequestDto</a></code>
</td>
@ -182,7 +176,7 @@
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/object" target="_blank" >object</a></code>
<b>Returns : </b> <code><a href="../classes/EsResponseDto.html" target="_self" >Promise&lt;EsResponseDto&gt;</a></code>
</div>
<div class="io-description">
@ -214,14 +208,14 @@
<td class="col-md-4">
<b>Decorators : </b>
<br />
<code>@ApiOperation({summary: &#x27;Finds paper by its UUID.&#x27;})<br />@ApiResponse({status: 200, description: &#x27;Returns back acquired paper.&#x27;, type: SearchResultDto})<br />@Get(&#x27;:uuid&#x27;)<br />@UseInterceptors(PageInterceptor)<br />@HttpCode(200)<br /></code>
<code>@ApiTags(&#x27;Search&#x27;)<br />@ApiOperation({summary: &#x27;Finds paper by its UUID&#x27;, tags: undefined})<br />@ApiResponse({status: 200, description: &#x27;Returns back a paper&#x27;, type: PaperDto})<br />@ApiGatewayTimeoutResponse({description: &#x27;Elasticsearch request timed out&#x27;})<br />@Get(&#x27;:uuid&#x27;)<br />@HttpCode(200)<br /></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="56"
class="link-to-prism">src/application/controller/papers.controller.ts:56</a></div>
<div class="io-line">Defined in <a href="" data-line="72"
class="link-to-prism">src/application/controller/papers.controller.ts:72</a></div>
</td>
</tr>
@ -261,7 +255,7 @@
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/object" target="_blank" >object</a></code>
<b>Returns : </b> <code><a href="../classes/PaperDto.html" target="_self" >Promise&lt;PaperDto&gt;</a></code>
</div>
<div class="io-description">
@ -276,17 +270,21 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { Controller, Get, HttpCode, HttpException, Next, Param, ParseUUIDPipe, Put, Query, Req, Res, UseInterceptors } from &quot;@nestjs/common&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { Controller, Get, HttpCode, Param, ParseUUIDPipe, Req, UseInterceptors } from &quot;@nestjs/common&quot;;
import { SearchService } from &quot;../../core/services/common/search.service&quot;;
import { PageInterceptor } from &quot;src/core/interceptors/page.interceptor&quot;;
import { SearchResultDto } from &quot;src/core/domain/dtos/search-result.dto&quot;;
import { ApiOperation, ApiResponse } from &quot;@nestjs/swagger&quot;;
import { RequestDto } from &quot;src/core/domain/dtos/request.dto&quot;;
import { PageInterceptor } from &quot;../../core/interceptors/page.interceptor&quot;;
import { ApiExtraModels, ApiGatewayTimeoutResponse, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from &quot;@nestjs/swagger&quot;;
import { RequestDto } from &quot;../../core/domain/dtos/request.dto&quot;;
import { EsHitDto, EsResponseDto, PageDto, PaperDto } from &quot;../../core/domain&quot;;
/**
* /papers/ route controller
*/
@Controller(&#x27;papers&#x27;)
@Controller({
version: &#x27;1&#x27;,
path: &#x27;papers&#x27;,
})
@ApiExtraModels(RequestDto, EsHitDto, EsResponseDto)
export class PapersController {
constructor(private searchService: SearchService) {}
@ -296,22 +294,28 @@ export class PapersController {
* @param response
* @returns a response with a set of matching papers
*/
@ApiOperation({ summary: &#x27;Finds papers by context based on the query.&#x27; })
@ApiResponse({
status: 200,
description: &#x27;Returns back acquired papers.&#x27;,
type: SearchResultDto,
})
@ApiTags(&#x27;Search&#x27;)
@ApiOperation({
summary: &#x27;Finds papers by context based on the query&#x27;,
})
@ApiResponse({
status: 200,
description: &#x27;Returns back a page with acquired papers&#x27;,
type: PageDto
})
@ApiGatewayTimeoutResponse({
description: &#x27;Elasticsearch request timed out&#x27;
})
@Get(&#x27;search&#x27;)
@UseInterceptors(PageInterceptor)
@HttpCode(200)
getByContext(@Req() query: RequestDto): object {
return this.searchService.findByContext(query.es_query).then(
(response: SearchResultDto) &#x3D;&gt; {
return response.data;
getByContext(@Req() request: RequestDto): Promise&lt;EsResponseDto&gt; {
return this.searchService.findByContext(request.es_query).then(
(response) &#x3D;&gt; {
return response;
},
(error: SearchResultDto) &#x3D;&gt; {
throw new HttpException(error.data, error.statusCode);
(error) &#x3D;&gt; {
throw error;
}
);
}
@ -322,22 +326,28 @@ export class PapersController {
* @param response
* @returns a response with a requested object
*/
@ApiOperation({ summary: &#x27;Finds paper by its UUID.&#x27; })
@ApiResponse({
status: 200,
description: &#x27;Returns back acquired paper.&#x27;,
type: SearchResultDto,
})
@ApiTags(&#x27;Search&#x27;)
@ApiOperation({
summary: &#x27;Finds paper by its UUID&#x27;,
tags: [&#x27;Search&#x27;]
})
@ApiResponse({
status: 200,
description: &#x27;Returns back a paper&#x27;,
type: PaperDto
})
@ApiGatewayTimeoutResponse({
description: &#x27;Elasticsearch request timed out&#x27;
})
@Get(&#x27;:uuid&#x27;)
@UseInterceptors(PageInterceptor)
@HttpCode(200)
getByID(@Param(&#x27;uuid&#x27;, ParseUUIDPipe) uuid: string): object {
getByID(@Param(&#x27;uuid&#x27;, ParseUUIDPipe) uuid: string): Promise&lt;PaperDto&gt; {
return this.searchService.findByID(uuid).then(
(response) &#x3D;&gt; {
return response.data;
(response: EsResponseDto) &#x3D;&gt; {
return response.hits.hits[0]._source;
},
(error) &#x3D;&gt; {
throw new HttpException(error.data, error.status);
throw error;
}
);
}

View File

@ -143,7 +143,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./classes/EsHitDto.html">src/core/domain/dtos/es-hit.dto.ts</a>
<a href="./classes/EsHitDto.html">src/core/domain/dtos/elastic/es-hit.dto.ts</a>
</td>
<td>class</td>
<td>EsHitDto</td>
@ -155,7 +155,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/es-hit.dto.ts</a>
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/elastic/es-hit.dto.ts</a>
</td>
<td>variable</td>
<td>allowedProperties</td>
@ -167,7 +167,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./classes/EsQueryDto.html">src/core/domain/dtos/es-query.dto.ts</a>
<a href="./classes/EsQueryDto.html">src/core/domain/dtos/elastic/es-query.dto.ts</a>
</td>
<td>class</td>
<td>EsQueryDto</td>
@ -179,7 +179,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/es-query.dto.ts</a>
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/elastic/es-query.dto.ts</a>
</td>
<td>variable</td>
<td>allowedProperties</td>
@ -191,7 +191,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./classes/EsResponseDto.html">src/core/domain/dtos/es-response.dto.ts</a>
<a href="./classes/EsResponseDto.html">src/core/domain/dtos/elastic/es-response.dto.ts</a>
</td>
<td>class</td>
<td>EsResponseDto</td>
@ -203,7 +203,31 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/es-response.dto.ts</a>
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/elastic/es-response.dto.ts</a>
</td>
<td>variable</td>
<td>allowedProperties</td>
<td align="right" data-sort="100">
<span class="coverage-percent">100 %</span>
<span class="coverage-count">(1/1)</span>
</td>
</tr>
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./classes/PageMetaDto.html">src/core/domain/dtos/page-meta.dto.ts</a>
</td>
<td>class</td>
<td>PageMetaDto</td>
<td align="right" data-sort="100">
<span class="coverage-percent">100 %</span>
<span class="coverage-count">(7/7)</span>
</td>
</tr>
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./miscellaneous/variables.html#allowedProperties">src/core/domain/dtos/page-meta.dto.ts</a>
</td>
<td>variable</td>
<td>allowedProperties</td>
@ -335,7 +359,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./interfaces/EsPit.html">src/core/domain/interfaces/es-pit.interface.ts</a>
<a href="./interfaces/EsPit.html">src/core/domain/interfaces/elastic/es-pit.interface.ts</a>
</td>
<td>interface</td>
<td>EsPit</td>
@ -347,7 +371,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./interfaces/EqQueryString.html">src/core/domain/interfaces/es-query-string.interface.ts</a>
<a href="./interfaces/EqQueryString.html">src/core/domain/interfaces/elastic/es-query-string.interface.ts</a>
</td>
<td>interface</td>
<td>EqQueryString</td>
@ -359,7 +383,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./interfaces/EsQuery.html">src/core/domain/interfaces/es-query.interface.ts</a>
<a href="./interfaces/EsQuery.html">src/core/domain/interfaces/elastic/es-query.interface.ts</a>
</td>
<td>interface</td>
<td>EsQuery</td>
@ -371,7 +395,7 @@
<tr class="very-good">
<td>
<!-- miscellaneous -->
<a href="./interfaces/EsResponseHits.html">src/core/domain/interfaces/es-response-hits.interface.ts</a>
<a href="./interfaces/EsResponseHits.html">src/core/domain/interfaces/elastic/es-response-hits.interface.ts</a>
</td>
<td>interface</td>
<td>EsResponseHits</td>
@ -545,7 +569,7 @@
<td>PageInterceptor</td>
<td align="right" data-sort="100">
<span class="coverage-percent">100 %</span>
<span class="coverage-count">(7/7)</span>
<span class="coverage-count">(8/8)</span>
</td>
</tr>
<tr class="very-good">
@ -593,7 +617,7 @@
<td>SearchService</td>
<td align="right" data-sort="100">
<span class="coverage-percent">100 %</span>
<span class="coverage-count">(5/5)</span>
<span class="coverage-count">(6/6)</span>
</td>
</tr>
<tr class="very-good">

View File

@ -4,217 +4,217 @@
<!-- Generated by graphviz version 5.0.0 (20220707.1540)
-->
<!-- Title: dependencies Pages: 1 -->
<svg width="1164pt" height="261pt"
viewBox="0.00 0.00 1164.00 260.80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 256.8)">
<svg width="924pt" height="329pt"
viewBox="0.00 0.00 924.00 328.80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 324.8)">
<title>dependencies</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-256.8 1160,-256.8 1160,4 -4,4"/>
<text text-anchor="middle" x="578" y="-8.2" font-family="sans-serif" font-size="14.00">dependencies</text>
<g id="clust1" class="cluster">
<title>cluster_AppModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-32.8 8,-160.8 268,-160.8 268,-32.8 8,-32.8"/>
</g>
<g id="clust3" class="cluster">
<title>cluster_AppModule_imports</title>
<polygon fill="none" stroke="black" points="16,-40.8 16,-92.8 260,-92.8 260,-40.8 16,-40.8"/>
</g>
<g id="clust7" class="cluster">
<title>cluster_CommonModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="276,-100.8 276,-168.8 852,-168.8 852,-100.8 276,-100.8"/>
</g>
<g id="clust9" class="cluster">
<title>cluster_CommonModule_imports</title>
<polygon fill="none" stroke="black" points="572,-108.8 572,-160.8 844,-160.8 844,-108.8 572,-108.8"/>
</g>
<g id="clust10" class="cluster">
<title>cluster_CommonModule_exports</title>
<polygon fill="none" stroke="black" points="284,-108.8 284,-160.8 564,-160.8 564,-108.8 284,-108.8"/>
</g>
<g id="clust19" class="cluster">
<title>cluster_HttpResponseModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="330,-176.8 330,-244.8 714,-244.8 714,-176.8 330,-176.8"/>
</g>
<g id="clust22" class="cluster">
<title>cluster_HttpResponseModule_exports</title>
<polygon fill="none" stroke="black" points="550,-184.8 550,-236.8 706,-236.8 706,-184.8 550,-184.8"/>
</g>
<g id="clust24" class="cluster">
<title>cluster_HttpResponseModule_providers</title>
<polygon fill="none" stroke="black" points="338,-184.8 338,-236.8 542,-236.8 542,-184.8 338,-184.8"/>
</g>
<g id="clust25" class="cluster">
<title>cluster_LoggerModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="722,-176.8 722,-244.8 1016,-244.8 1016,-176.8 722,-176.8"/>
</g>
<g id="clust28" class="cluster">
<title>cluster_LoggerModule_exports</title>
<polygon fill="none" stroke="black" points="890,-184.8 890,-236.8 1008,-236.8 1008,-184.8 890,-184.8"/>
</g>
<g id="clust30" class="cluster">
<title>cluster_LoggerModule_providers</title>
<polygon fill="none" stroke="black" points="730,-184.8 730,-236.8 882,-236.8 882,-184.8 730,-184.8"/>
</g>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-324.8 920,-324.8 920,4 -4,4"/>
<text text-anchor="middle" x="458" y="-8.2" font-family="sans-serif" font-size="14.00">dependencies</text>
<g id="clust31" class="cluster">
<title>cluster_SearchModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="860,-100.8 860,-168.8 1148,-168.8 1148,-100.8 860,-100.8"/>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="744,-32.8 744,-236.8 908,-236.8 908,-32.8 744,-32.8"/>
</g>
<g id="clust34" class="cluster">
<title>cluster_SearchModule_exports</title>
<polygon fill="none" stroke="black" points="1024,-108.8 1024,-160.8 1140,-160.8 1140,-108.8 1024,-108.8"/>
<polygon fill="none" stroke="black" points="760,-176.8 760,-228.8 876,-228.8 876,-176.8 760,-176.8"/>
</g>
<g id="clust36" class="cluster">
<title>cluster_SearchModule_providers</title>
<polygon fill="none" stroke="black" points="868,-108.8 868,-160.8 1016,-160.8 1016,-108.8 868,-108.8"/>
<polygon fill="none" stroke="black" points="752,-40.8 752,-92.8 900,-92.8 900,-40.8 752,-40.8"/>
</g>
<g id="clust1" class="cluster">
<title>cluster_AppModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="592,-100.8 592,-228.8 736,-228.8 736,-100.8 592,-100.8"/>
</g>
<g id="clust3" class="cluster">
<title>cluster_AppModule_imports</title>
<polygon fill="none" stroke="black" points="600,-108.8 600,-160.8 728,-160.8 728,-108.8 600,-108.8"/>
</g>
<g id="clust7" class="cluster">
<title>cluster_CommonModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-168.8 8,-236.8 584,-236.8 584,-168.8 8,-168.8"/>
</g>
<g id="clust9" class="cluster">
<title>cluster_CommonModule_imports</title>
<polygon fill="none" stroke="black" points="304,-176.8 304,-228.8 576,-228.8 576,-176.8 304,-176.8"/>
</g>
<g id="clust10" class="cluster">
<title>cluster_CommonModule_exports</title>
<polygon fill="none" stroke="black" points="16,-176.8 16,-228.8 296,-228.8 296,-176.8 16,-176.8"/>
</g>
<g id="clust19" class="cluster">
<title>cluster_HttpResponseModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="62,-244.8 62,-312.8 446,-312.8 446,-244.8 62,-244.8"/>
</g>
<g id="clust22" class="cluster">
<title>cluster_HttpResponseModule_exports</title>
<polygon fill="none" stroke="black" points="282,-252.8 282,-304.8 438,-304.8 438,-252.8 282,-252.8"/>
</g>
<g id="clust24" class="cluster">
<title>cluster_HttpResponseModule_providers</title>
<polygon fill="none" stroke="black" points="70,-252.8 70,-304.8 274,-304.8 274,-252.8 70,-252.8"/>
</g>
<g id="clust25" class="cluster">
<title>cluster_LoggerModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="454,-244.8 454,-312.8 748,-312.8 748,-244.8 454,-244.8"/>
</g>
<g id="clust28" class="cluster">
<title>cluster_LoggerModule_exports</title>
<polygon fill="none" stroke="black" points="622,-252.8 622,-304.8 740,-304.8 740,-252.8 622,-252.8"/>
</g>
<g id="clust30" class="cluster">
<title>cluster_LoggerModule_providers</title>
<polygon fill="none" stroke="black" points="462,-252.8 462,-304.8 614,-304.8 614,-252.8 462,-252.8"/>
</g>
<!-- CommonModule -->
<g id="node1" class="node">
<title>CommonModule</title>
<polygon fill="#8dd3c7" stroke="black" points="135.67,-84.8 132.67,-88.8 111.67,-88.8 108.67,-84.8 24.33,-84.8 24.33,-48.8 135.67,-48.8 135.67,-84.8"/>
<text text-anchor="middle" x="80" y="-62.6" font-family="Times,serif" font-size="14.00">CommonModule</text>
<polygon fill="#8dd3c7" stroke="black" points="719.67,-152.8 716.67,-156.8 695.67,-156.8 692.67,-152.8 608.33,-152.8 608.33,-116.8 719.67,-116.8 719.67,-152.8"/>
<text text-anchor="middle" x="664" y="-130.6" font-family="Times,serif" font-size="14.00">CommonModule</text>
</g>
<!-- AppModule -->
<g id="node3" class="node">
<title>AppModule</title>
<polygon fill="#8dd3c7" stroke="black" points="182.66,-152.8 179.66,-156.8 158.66,-156.8 155.66,-152.8 99.34,-152.8 99.34,-116.8 182.66,-116.8 182.66,-152.8"/>
<text text-anchor="middle" x="141" y="-130.6" font-family="Times,serif" font-size="14.00">AppModule</text>
<polygon fill="#8dd3c7" stroke="black" points="716.66,-220.8 713.66,-224.8 692.66,-224.8 689.66,-220.8 633.34,-220.8 633.34,-184.8 716.66,-184.8 716.66,-220.8"/>
<text text-anchor="middle" x="675" y="-198.6" font-family="Times,serif" font-size="14.00">AppModule</text>
</g>
<!-- CommonModule&#45;&gt;AppModule -->
<g id="edge1" class="edge">
<title>CommonModule&#45;&gt;AppModule</title>
<path fill="none" stroke="black" d="M105.28,-85.02C105.28,-85.02 105.28,-106.53 105.28,-106.53"/>
<polygon fill="black" stroke="black" points="101.78,-106.53 105.28,-116.53 108.78,-106.53 101.78,-106.53"/>
<path fill="none" stroke="black" d="M675,-153.02C675,-153.02 675,-174.53 675,-174.53"/>
<polygon fill="black" stroke="black" points="671.5,-174.53 675,-184.53 678.5,-174.53 671.5,-174.53"/>
</g>
<!-- HttpResponseModule -->
<g id="node6" class="node">
<title>HttpResponseModule </title>
<polygon fill="#fb8072" stroke="black" points="555.6,-152.8 414.4,-152.8 414.4,-116.8 555.6,-116.8 555.6,-152.8"/>
<text text-anchor="middle" x="485" y="-130.6" font-family="Times,serif" font-size="14.00">HttpResponseModule </text>
<polygon fill="#fb8072" stroke="black" points="287.6,-220.8 146.4,-220.8 146.4,-184.8 287.6,-184.8 287.6,-220.8"/>
<text text-anchor="middle" x="217" y="-198.6" font-family="Times,serif" font-size="14.00">HttpResponseModule </text>
</g>
<!-- CommonModule&#45;&gt;HttpResponseModule -->
<g id="edge5" class="edge">
<title>CommonModule&#45;&gt;HttpResponseModule </title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M129.72,-84.91C129.72,-94.7 129.72,-104.8 129.72,-104.8 129.72,-104.8 485,-104.8 485,-104.8 485,-104.8 485,-106.66 485,-106.66"/>
<polygon fill="black" stroke="black" points="481.5,-106.66 485,-116.66 488.5,-106.66 481.5,-106.66"/>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M608.29,-134.8C488.46,-134.8 217,-134.8 217,-134.8 217,-134.8 217,-174.58 217,-174.58"/>
<polygon fill="black" stroke="black" points="213.5,-174.58 217,-184.58 220.5,-174.58 213.5,-174.58"/>
</g>
<!-- LoggerModule -->
<g id="node7" class="node">
<title>LoggerModule </title>
<polygon fill="#fb8072" stroke="black" points="395.98,-152.8 292.02,-152.8 292.02,-116.8 395.98,-116.8 395.98,-152.8"/>
<text text-anchor="middle" x="344" y="-130.6" font-family="Times,serif" font-size="14.00">LoggerModule </text>
<polygon fill="#fb8072" stroke="black" points="127.98,-220.8 24.02,-220.8 24.02,-184.8 127.98,-184.8 127.98,-220.8"/>
<text text-anchor="middle" x="76" y="-198.6" font-family="Times,serif" font-size="14.00">LoggerModule </text>
</g>
<!-- CommonModule&#45;&gt;LoggerModule -->
<g id="edge6" class="edge">
<title>CommonModule&#45;&gt;LoggerModule </title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M123.61,-84.95C123.61,-97.07 123.61,-110.8 123.61,-110.8 123.61,-110.8 344,-110.8 344,-110.8 344,-110.8 344,-111.38 344,-111.38"/>
<polygon fill="black" stroke="black" points="340.5,-106.61 344,-116.61 347.5,-106.61 340.5,-106.61"/>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M608.15,-125.8C461.38,-125.8 76,-125.8 76,-125.8 76,-125.8 76,-174.55 76,-174.55"/>
<polygon fill="black" stroke="black" points="72.5,-174.55 76,-184.55 79.5,-174.55 72.5,-174.55"/>
</g>
<!-- SearchModule -->
<g id="node2" class="node">
<title>SearchModule</title>
<polygon fill="#8dd3c7" stroke="black" points="251.64,-84.8 248.64,-88.8 227.64,-88.8 224.64,-84.8 154.36,-84.8 154.36,-48.8 251.64,-48.8 251.64,-84.8"/>
<text text-anchor="middle" x="203" y="-62.6" font-family="Times,serif" font-size="14.00">SearchModule</text>
<polygon fill="#8dd3c7" stroke="black" points="866.64,-152.8 863.64,-156.8 842.64,-156.8 839.64,-152.8 769.36,-152.8 769.36,-116.8 866.64,-116.8 866.64,-152.8"/>
<text text-anchor="middle" x="818" y="-130.6" font-family="Times,serif" font-size="14.00">SearchModule</text>
</g>
<!-- SearchModule&#45;&gt;AppModule -->
<g id="edge2" class="edge">
<title>SearchModule&#45;&gt;AppModule</title>
<path fill="none" stroke="black" d="M168.51,-85.02C168.51,-85.02 168.51,-106.53 168.51,-106.53"/>
<polygon fill="black" stroke="black" points="165.01,-106.53 168.51,-116.53 172.01,-106.53 165.01,-106.53"/>
<path fill="none" stroke="black" d="M769.07,-134.8C756.2,-134.8 745.84,-134.8 745.84,-134.8 745.84,-134.8 745.84,-202.8 745.84,-202.8 745.84,-202.8 727.03,-202.8 727.03,-202.8"/>
<polygon fill="black" stroke="black" points="727.03,-199.3 717.03,-202.8 727.03,-206.3 727.03,-199.3"/>
</g>
<!-- SearchService -->
<g id="node12" class="node">
<title>SearchService </title>
<polygon fill="#fb8072" stroke="black" points="1131.57,-152.8 1032.43,-152.8 1032.43,-116.8 1131.57,-116.8 1131.57,-152.8"/>
<text text-anchor="middle" x="1082" y="-130.6" font-family="Times,serif" font-size="14.00">SearchService </text>
<polygon fill="#fb8072" stroke="black" points="867.57,-220.8 768.43,-220.8 768.43,-184.8 867.57,-184.8 867.57,-220.8"/>
<text text-anchor="middle" x="818" y="-198.6" font-family="Times,serif" font-size="14.00">SearchService </text>
</g>
<!-- SearchModule&#45;&gt;SearchService -->
<g id="edge11" class="edge">
<title>SearchModule&#45;&gt;SearchService </title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M251.83,-60.8C436.52,-60.8 1082,-60.8 1082,-60.8 1082,-60.8 1082,-106.51 1082,-106.51"/>
<polygon fill="black" stroke="black" points="1078.5,-106.51 1082,-116.51 1085.5,-106.51 1078.5,-106.51"/>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M818,-153.02C818,-153.02 818,-174.53 818,-174.53"/>
<polygon fill="black" stroke="black" points="814.5,-174.53 818,-184.53 821.5,-174.53 814.5,-174.53"/>
</g>
<!-- HttpResponseModule -->
<g id="node4" class="node">
<title>HttpResponseModule</title>
<polygon fill="#8dd3c7" stroke="black" points="718.1,-152.8 715.1,-156.8 694.1,-156.8 691.1,-152.8 579.9,-152.8 579.9,-116.8 718.1,-116.8 718.1,-152.8"/>
<text text-anchor="middle" x="649" y="-130.6" font-family="Times,serif" font-size="14.00">HttpResponseModule</text>
<polygon fill="#8dd3c7" stroke="black" points="450.1,-220.8 447.1,-224.8 426.1,-224.8 423.1,-220.8 311.9,-220.8 311.9,-184.8 450.1,-184.8 450.1,-220.8"/>
<text text-anchor="middle" x="381" y="-198.6" font-family="Times,serif" font-size="14.00">HttpResponseModule</text>
</g>
<!-- HttpResponseModule&#45;&gt;CommonModule -->
<g id="edge3" class="edge">
<title>HttpResponseModule&#45;&gt;CommonModule</title>
<path fill="none" stroke="black" d="M649,-116.73C649,-107.34 649,-97.8 649,-97.8 649,-97.8 111.39,-97.8 111.39,-97.8 111.39,-97.8 111.39,-94.83 111.39,-94.83"/>
<polygon fill="black" stroke="black" points="114.89,-94.83 111.39,-84.83 107.89,-94.83 114.89,-94.83"/>
<path fill="none" stroke="black" d="M381,-184.78C381,-167.43 381,-143.8 381,-143.8 381,-143.8 598.09,-143.8 598.09,-143.8"/>
<polygon fill="black" stroke="black" points="598.09,-147.3 608.09,-143.8 598.09,-140.3 598.09,-147.3"/>
</g>
<!-- HttpResponseService -->
<g id="node8" class="node">
<title>HttpResponseService </title>
<polygon fill="#fb8072" stroke="black" points="698.03,-228.8 557.97,-228.8 557.97,-192.8 698.03,-192.8 698.03,-228.8"/>
<text text-anchor="middle" x="628" y="-206.6" font-family="Times,serif" font-size="14.00">HttpResponseService </text>
<polygon fill="#fb8072" stroke="black" points="430.03,-296.8 289.97,-296.8 289.97,-260.8 430.03,-260.8 430.03,-296.8"/>
<text text-anchor="middle" x="360" y="-274.6" font-family="Times,serif" font-size="14.00">HttpResponseService </text>
</g>
<!-- HttpResponseModule&#45;&gt;HttpResponseService -->
<g id="edge7" class="edge">
<title>HttpResponseModule&#45;&gt;HttpResponseService </title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M658.66,-152.81C658.66,-152.81 658.66,-182.65 658.66,-182.65"/>
<polygon fill="black" stroke="black" points="655.16,-182.65 658.66,-192.65 662.16,-182.65 655.16,-182.65"/>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M390.66,-220.81C390.66,-220.81 390.66,-250.65 390.66,-250.65"/>
<polygon fill="black" stroke="black" points="387.16,-250.65 390.66,-260.65 394.16,-250.65 387.16,-250.65"/>
</g>
<!-- LoggerModule -->
<g id="node5" class="node">
<title>LoggerModule</title>
<polygon fill="#8dd3c7" stroke="black" points="835.98,-152.8 832.98,-156.8 811.98,-156.8 808.98,-152.8 736.02,-152.8 736.02,-116.8 835.98,-116.8 835.98,-152.8"/>
<text text-anchor="middle" x="786" y="-130.6" font-family="Times,serif" font-size="14.00">LoggerModule</text>
<polygon fill="#8dd3c7" stroke="black" points="567.98,-220.8 564.98,-224.8 543.98,-224.8 540.98,-220.8 468.02,-220.8 468.02,-184.8 567.98,-184.8 567.98,-220.8"/>
<text text-anchor="middle" x="518" y="-198.6" font-family="Times,serif" font-size="14.00">LoggerModule</text>
</g>
<!-- LoggerModule&#45;&gt;CommonModule -->
<g id="edge4" class="edge">
<title>LoggerModule&#45;&gt;CommonModule</title>
<path fill="none" stroke="black" d="M786,-116.31C786,-104.66 786,-91.8 786,-91.8 786,-91.8 117.5,-91.8 117.5,-91.8 117.5,-91.8 117.5,-91.12 117.5,-91.12"/>
<polygon fill="black" stroke="black" points="121,-94.97 117.5,-84.97 114,-94.97 121,-94.97"/>
<path fill="none" stroke="black" d="M568.23,-196.8C595.11,-196.8 622.75,-196.8 622.75,-196.8 622.75,-196.8 622.75,-163.07 622.75,-163.07"/>
<polygon fill="black" stroke="black" points="626.25,-163.07 622.75,-153.07 619.25,-163.07 626.25,-163.07"/>
</g>
<!-- LoggerService -->
<g id="node10" class="node">
<title>LoggerService </title>
<polygon fill="#fb8072" stroke="black" points="999.91,-228.8 898.09,-228.8 898.09,-192.8 999.91,-192.8 999.91,-228.8"/>
<text text-anchor="middle" x="949" y="-206.6" font-family="Times,serif" font-size="14.00">LoggerService </text>
<polygon fill="#fb8072" stroke="black" points="731.91,-296.8 630.09,-296.8 630.09,-260.8 731.91,-260.8 731.91,-296.8"/>
<text text-anchor="middle" x="681" y="-274.6" font-family="Times,serif" font-size="14.00">LoggerService </text>
</g>
<!-- LoggerModule&#45;&gt;LoggerService -->
<g id="edge9" class="edge">
<title>LoggerModule&#45;&gt;LoggerService </title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M803.37,-152.84C803.37,-165.36 803.37,-179.8 803.37,-179.8 803.37,-179.8 949,-179.8 949,-179.8 949,-179.8 949,-182.77 949,-182.77"/>
<polygon fill="black" stroke="black" points="945.5,-182.77 949,-192.77 952.5,-182.77 945.5,-182.77"/>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M568.16,-208.8C592.04,-208.8 615.46,-208.8 615.46,-208.8 615.46,-208.8 615.46,-278.8 615.46,-278.8 615.46,-278.8 620,-278.8 620,-278.8"/>
<polygon fill="black" stroke="black" points="620,-282.3 630,-278.8 620,-275.3 620,-282.3"/>
</g>
<!-- HttpResponseService -->
<g id="node9" class="node">
<title>HttpResponseService</title>
<ellipse fill="#fdb462" stroke="black" cx="440" cy="-210.8" rx="94.35" ry="18"/>
<text text-anchor="middle" x="440" y="-206.6" font-family="Times,serif" font-size="14.00">HttpResponseService</text>
<ellipse fill="#fdb462" stroke="black" cx="172" cy="-278.8" rx="94.35" ry="18"/>
<text text-anchor="middle" x="172" y="-274.6" font-family="Times,serif" font-size="14.00">HttpResponseService</text>
</g>
<!-- HttpResponseService&#45;&gt;HttpResponseModule -->
<g id="edge8" class="edge">
<title>HttpResponseService&#45;&gt;HttpResponseModule</title>
<path fill="none" stroke="black" d="M474.19,-193.91C474.19,-181.51 474.19,-166.8 474.19,-166.8 474.19,-166.8 619.3,-166.8 619.3,-166.8 619.3,-166.8 619.3,-163.04 619.3,-163.04"/>
<polygon fill="black" stroke="black" points="622.8,-163.04 619.3,-153.04 615.8,-163.04 622.8,-163.04"/>
<path fill="none" stroke="black" d="M206.19,-262.02C206.19,-251.84 206.19,-240.8 206.19,-240.8 206.19,-240.8 351.3,-240.8 351.3,-240.8 351.3,-240.8 351.3,-230.91 351.3,-230.91"/>
<polygon fill="black" stroke="black" points="354.8,-230.91 351.3,-220.91 347.8,-230.91 354.8,-230.91"/>
</g>
<!-- LoggerService -->
<g id="node11" class="node">
<title>LoggerService</title>
<ellipse fill="#fdb462" stroke="black" cx="806" cy="-210.8" rx="67.76" ry="18"/>
<text text-anchor="middle" x="806" y="-206.6" font-family="Times,serif" font-size="14.00">LoggerService</text>
<ellipse fill="#fdb462" stroke="black" cx="538" cy="-278.8" rx="67.76" ry="18"/>
<text text-anchor="middle" x="538" y="-274.6" font-family="Times,serif" font-size="14.00">LoggerService</text>
</g>
<!-- LoggerService&#45;&gt;LoggerModule -->
<g id="edge10" class="edge">
<title>LoggerService&#45;&gt;LoggerModule</title>
<path fill="none" stroke="black" d="M770.74,-195.05C770.74,-195.05 770.74,-162.93 770.74,-162.93"/>
<polygon fill="black" stroke="black" points="774.24,-162.93 770.74,-152.93 767.24,-162.93 774.24,-162.93"/>
<path fill="none" stroke="black" d="M519.06,-261.17C519.06,-261.17 519.06,-230.86 519.06,-230.86"/>
<polygon fill="black" stroke="black" points="522.56,-230.86 519.06,-220.86 515.56,-230.86 522.56,-230.86"/>
</g>
<!-- SearchService -->
<g id="node13" class="node">
<title>SearchService</title>
<ellipse fill="#fdb462" stroke="black" cx="942" cy="-134.8" rx="66.03" ry="18"/>
<text text-anchor="middle" x="942" y="-130.6" font-family="Times,serif" font-size="14.00">SearchService</text>
<ellipse fill="#fdb462" stroke="black" cx="826" cy="-66.8" rx="66.03" ry="18"/>
<text text-anchor="middle" x="826" y="-62.6" font-family="Times,serif" font-size="14.00">SearchService</text>
</g>
<!-- SearchService&#45;&gt;SearchModule -->
<g id="edge12" class="edge">
<title>SearchService&#45;&gt;SearchModule</title>
<path fill="none" stroke="black" d="M942,-116.53C942,-98.24 942,-72.8 942,-72.8 942,-72.8 261.77,-72.8 261.77,-72.8"/>
<polygon fill="black" stroke="black" points="261.77,-69.3 251.77,-72.8 261.77,-76.3 261.77,-69.3"/>
<path fill="none" stroke="black" d="M818,-85.02C818,-85.02 818,-106.53 818,-106.53"/>
<polygon fill="black" stroke="black" points="814.5,-106.53 818,-116.53 821.5,-106.53 814.5,-106.53"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -85,6 +85,11 @@
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
<a href="#ES_IP" >ES_IP</a>
</li>
<li>
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
@ -142,7 +147,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="59" class="link-to-prism">src/core/interceptors/page.interceptor.ts:59</a></div>
<div class="io-line">Defined in <a href="" data-line="73" class="link-to-prism">src/core/interceptors/page.interceptor.ts:73</a></div>
</td>
</tr>
@ -209,8 +214,8 @@
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="180"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:180</a></div>
<div class="io-line">Defined in <a href="" data-line="206"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:206</a></div>
</td>
</tr>
@ -289,8 +294,8 @@
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="160"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:160</a></div>
<div class="io-line">Defined in <a href="" data-line="186"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:186</a></div>
</td>
</tr>
@ -388,8 +393,8 @@
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="75"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:75</a></div>
<div class="io-line">Defined in <a href="" data-line="89"
class="link-to-prism">src/core/interceptors/page.interceptor.ts:89</a></div>
</td>
</tr>
@ -458,6 +463,39 @@
<h3 id="inputs">
Properties
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="ES_IP"></a>
<span class="name">
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
<span ><b>ES_IP</b></span>
<a href="#ES_IP"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>process.env.ES_CONTAINER_NAME</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="174" class="link-to-prism">src/core/interceptors/page.interceptor.ts:174</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Elastichsearch IP address</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
@ -478,7 +516,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="148" class="link-to-prism">src/core/interceptors/page.interceptor.ts:148</a></div>
<div class="io-line">Defined in <a href="" data-line="169" class="link-to-prism">src/core/interceptors/page.interceptor.ts:169</a></div>
</td>
</tr>
@ -511,7 +549,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="153" class="link-to-prism">src/core/interceptors/page.interceptor.ts:153</a></div>
<div class="io-line">Defined in <a href="" data-line="179" class="link-to-prism">src/core/interceptors/page.interceptor.ts:179</a></div>
</td>
</tr>
@ -532,24 +570,20 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { HttpService } from &quot;@nestjs/axios&quot;;
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from &quot;@nestjs/common&quot;;
import { reverse } from &quot;dns&quot;;
import { Observable, map, take } from &quot;rxjs&quot;;
import { EsResponseDto, PageDto } from &quot;../domain/dtos&quot;;
import { EsQueryDto } from &quot;../domain/dtos/es-query.dto&quot;;
import { PageDto } from &quot;../domain/dtos&quot;;
import { EsQueryDto } from &quot;../domain/dtos/elastic/es-query.dto&quot;;
import { RequestDto } from &quot;../domain/dtos/request.dto&quot;;
import { SearchQueryDto } from &quot;../domain/dtos/search-q.dto&quot;;
import { SearchResultDto } from &quot;../domain/dtos/search-result.dto&quot;;
import { EsTime } from &quot;../domain/enums/es-time.enum&quot;;
import { Order } from &quot;../domain/enums/page-order.enum&quot;;
import { PageMeta } from &quot;../domain/interfaces&quot;;
import { EsPit } from &quot;../domain/interfaces/es-pit.interface&quot;;
import { SearchInfo } from &quot;../domain/interfaces/search-info.interface&quot;;
import { SearchService } from &quot;../services/common/search.service&quot;;
import { EsPit } from &quot;../domain/interfaces/elastic/es-pit.interface&quot;;
/**
* Previous search data storage
*/
class PrevSearch implements SearchInfo {
class PrevSearch {
/**
* Constructs an uninitialized object
*/
@ -562,17 +596,35 @@ class PrevSearch implements SearchInfo {
/**
* PIT object of the previous search
*/
pit: EsPit;
private pit: EsPit;
set _pit(pit: EsPit) {
this.pit &#x3D; pit;
}
get _pit(): EsPit {
return this.pit;
}
/**
* Tiebreaker and sort parameters
*/
tiebreaker: unknown[];
private tiebreaker: unknown[];
set _tiebreaker(tiebreaker: unknown[]) {
this.tiebreaker &#x3D; tiebreaker;
}
get _tiebreaker(): unknown[] {
return this.tiebreaker;
}
/**
* Number of the previous page
*/
prevPage: number;
private prevPage: number;
set _prevPage(page: number) {
this.prevPage &#x3D; page;
}
get _prevPage(): number {
return this.prevPage;
}
/**
* Checks if there was the search before current one
@ -623,22 +675,26 @@ export class PageInterceptor implements NestInterceptor {
];
if (this.prevSearch.isSet()) {
request.es_query.pit &#x3D; this.prevSearch.pit;
request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
request.es_query.pit &#x3D; this.prevSearch._pit;
request.es_query.search_after &#x3D; this.prevSearch._tiebreaker;
let limit &#x3D; !query?.limit ? 10 : query.limit;
request.es_query.size &#x3D; limit * Math.abs(query.page - this.prevSearch.prevPage);
request.es_query.size &#x3D; limit * Math.abs(query.page - this.prevSearch._prevPage);
if (query.page &lt; this.prevSearch.prevPage) {
if (query.page &lt; this.prevSearch._prevPage) {
request.es_query.sort &#x3D; [{ _score: { order: &#x27;asc&#x27; } }];
request.es_query.size +&#x3D; limit - 1;
reverse &#x3D; true;
} else if (query.page &#x3D;&#x3D; this.prevSearch.prevPage) {
//...
} else if (query.page &#x3D;&#x3D; this.prevSearch._prevPage) {
// Caching should be HERE
request.es_query.sort &#x3D; [{ _score: { order: &#x27;asc&#x27; } }];
reverse &#x3D; true;
}
} else {
this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
request.es_query.size &#x3D; !query?.limit ? 10 : query.limit;
this.prevSearch._pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
let limit &#x3D; !query?.limit ? 10 : query.limit;
request.es_query.size &#x3D; limit * query.page;
}
return next.handle().pipe(
@ -646,28 +702,31 @@ export class PageInterceptor implements NestInterceptor {
// Setting the page meta-data
let meta: PageMeta &#x3D; {
total: res.hits.total.value,
pagenum: !query?.page ? 1 : query.page,
pagenum: !query?.page ? 1 : +query.page,
order: query?.order?.toUpperCase() &#x3D;&#x3D;&#x3D; Order.ASC ? Order.ASC : Order.DESC,
hasNext: false,
hasPrev: false,
pagesize: !query?.limit ? 10 : query.limit,
hasNext: undefined,
hasPrev: undefined,
};
// meta.hasNext &#x3D; res.hits.hits[meta.pagenum * meta.pagesize] ? true : false;
// meta.hasPrev &#x3D; res.hits.hits[(meta.pagenum - 1) * meta.pagesize - 1] ? true: false;
meta.hasNext &#x3D; meta.pagenum * meta.pagesize &lt; meta.total ? true : false;
meta.hasPrev &#x3D; meta.pagenum !&#x3D; 1 ? true : false;
// Saving the search info
this.prevSearch.pit.id &#x3D; res.pit_id;
this.prevSearch.tiebreaker &#x3D; res.hits.hits[res.hits.hits.length - 1].sort;
this.prevSearch.prevPage &#x3D; query.page;
this.prevSearch._pit.id &#x3D; res.pit_id;
this.prevSearch._tiebreaker &#x3D; res.hits.hits[res.hits.hits.length - 1]?.sort;
this.prevSearch._prevPage &#x3D; query.page;
// Check if the performed search is a backwards search
let data &#x3D; res.hits.hits.slice(-meta.pagesize);
if (reverse) {
console.log(&#x27;REVERSE&#x27;);
this.prevSearch.tiebreaker &#x3D; data[0].sort;
this.prevSearch._tiebreaker &#x3D; data[0]?.sort;
data.reverse();
reverse &#x3D; false;
}
// Omitting the redundant info and leaving only the document
data &#x3D; data.map((el) &#x3D;&gt; el._source);
// Return the page
return new PageDto(data, meta);
})
@ -679,6 +738,11 @@ export class PageInterceptor implements NestInterceptor {
*/
private readonly ES_PORT &#x3D; process.env.ES_PORT;
/**
* Elastichsearch IP address
*/
private readonly ES_IP &#x3D; process.env.ES_CONTAINER_NAME;
/**
* Info about previously completed search
*/
@ -692,12 +756,12 @@ export class PageInterceptor implements NestInterceptor {
public async getPIT(alive: number, unit: EsTime &#x3D; EsTime.min): Promise&lt;EsPit&gt; {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
(this.httpService.post&lt;EsPit&gt;(&#x60;http://localhost:${this.ES_PORT}/papers/_pit?keep_alive&#x3D;${alive+unit}&#x60;)
this.httpService.post&lt;EsPit&gt;(&#x60;http://${this.ES_IP}:${this.ES_PORT}/papers/_pit?keep_alive&#x3D;${alive+unit}&#x60;)
.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
.subscribe((res) &#x3D;&gt; {
.subscribe((res: EsPit) &#x3D;&gt; {
res.keep_alive &#x3D; alive + unit;
resolve(res);
}));
});
} catch (error) {
reject(error);
}
@ -712,7 +776,7 @@ export class PageInterceptor implements NestInterceptor {
async deletePIT(pitID: string): Promise&lt;boolean&gt; {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
this.httpService.delete(&#x60;http://localhost:${this.ES_PORT}/_pit&#x60;, {
this.httpService.delete(&#x60;http://${this.ES_IP}:${this.ES_PORT}/_pit&#x60;, {
data: { id: pitID },
headers: { &#x27;Content-Type&#x27;: &#x27;application/json&#x27; },
})
@ -725,72 +789,7 @@ export class PageInterceptor implements NestInterceptor {
}
})
}
}
/*
public saveInfo(pit: EsPit, tiebreaker: unknown[], page: number) {
this.pit.id &#x3D; pit.id;
this.pit.keep_alive &#x3D; pit.keep_alive;
this.tiebreaker &#x3D; tiebreaker.slice();
this.prevPage &#x3D; page;
}
public clearInfo() {
this.pit &#x3D; undefined;
this.tiebreaker &#x3D; undefined;
this.prevPage &#x3D; -1;
}*/
// getQueryParams(str: string): any {
// let parameters: object &#x3D; {};
// let pairs: string[] &#x3D; str.split(&#x27;,&#x27;);
// parameters[&#x27;main&#x27;] &#x3D; pairs[0];
// pairs.shift();
// if(!pairs || pairs[0] &#x3D;&#x3D;&#x3D; &#x27;&#x27;) return parameters;
// for (const pair of pairs) {
// const key: string &#x3D; pair.substring(0, pair.indexOf(&#x27;&#x3D;&#x27;));
// const value: string &#x3D; pair.substring(pair.indexOf(&#x27;&#x3D;&#x27;) + 1);
// parameters[key] &#x3D; value;
// }
// return parameters;
// }
/**
* OLD WAY PAGINATION
* // Setting the page data
// const data &#x3D; res.hits.slice((meta.pagenum - 1) * meta.pagesize, meta.pagenum * meta.pagesize);
*/
// if (query.page &#x3D;&#x3D; 1) {
// this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
// } else {
// if (!this.prevSearch.isSet()) {
// this.prevSearch.pit &#x3D; request.es_query.pit &#x3D; await this.getPIT(1);
// request.es_query.size &#x3D; query.limit * (query.page - 1);
// this.searchService.findByContext(request.es_query).then((res: SearchResultDto) &#x3D;&gt; {
// request.es_query.search_after &#x3D; res.data.hits.hits[res.data.hits.hits.length - 1].sort;
// });
// } else {
// if (query.page &#x3D;&#x3D; this.prevSearch.prevPage) {
// return;
// } else {
// request.es_query.pit &#x3D; this.prevSearch.pit;
// request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
// request.es_query.size &#x3D; (query.page - this.prevSearch.prevPage);
// }
// // request.es_query.pit &#x3D; this.prevSearch.pit;
// // request.es_query.search_after &#x3D; this.prevSearch.tiebreaker;
// }
// }
</code></pre>
}</code></pre>
</div>
</div>

View File

@ -85,6 +85,11 @@
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
<a href="#ES_IP" >ES_IP</a>
</li>
<li>
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
@ -133,7 +138,7 @@
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="14" class="link-to-prism">src/core/services/common/search.service.ts:14</a></div>
<div class="io-line">Defined in <a href="" data-line="11" class="link-to-prism">src/core/services/common/search.service.ts:11</a></div>
</td>
</tr>
@ -242,7 +247,7 @@ HTTPService instance</p>
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="../classes/SearchResultDto.html" target="_self" >Promise&lt;SearchResultDto&gt;</a></code>
<b>Returns : </b> <code><a href="../classes/EsResponseDto.html" target="_self" >Promise&lt;EsResponseDto&gt;</a></code>
</div>
<div class="io-description">
@ -275,8 +280,8 @@ HTTPService instance</p>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="32"
class="link-to-prism">src/core/services/common/search.service.ts:32</a></div>
<div class="io-line">Defined in <a href="" data-line="34"
class="link-to-prism">src/core/services/common/search.service.ts:34</a></div>
</td>
</tr>
@ -316,7 +321,7 @@ HTTPService instance</p>
<div>
</div>
<div class="io-description">
<b>Returns : </b> <code><a href="../classes/SearchResultDto.html" target="_self" >Promise&lt;SearchResultDto&gt;</a></code>
<b>Returns : </b> <code><a href="../classes/EsResponseDto.html" target="_self" >Promise&lt;EsResponseDto&gt;</a></code>
</div>
<div class="io-description">
@ -333,6 +338,39 @@ HTTPService instance</p>
<h3 id="inputs">
Properties
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="ES_IP"></a>
<span class="name">
<span class="modifier">Private</span>
<span class="modifier">Readonly</span>
<span ><b>ES_IP</b></span>
<a href="#ES_IP"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>process.env.ES_CONTAINER_NAME</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="27" class="link-to-prism">src/core/services/common/search.service.ts:27</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Elasticsearch IP address</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
@ -353,7 +391,7 @@ HTTPService instance</p>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="25" class="link-to-prism">src/core/services/common/search.service.ts:25</a></div>
<div class="io-line">Defined in <a href="" data-line="22" class="link-to-prism">src/core/services/common/search.service.ts:22</a></div>
</td>
</tr>
@ -373,13 +411,10 @@ HTTPService instance</p>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { HttpService } from &quot;@nestjs/axios&quot;;
import { GatewayTimeoutException, Injectable } from &quot;@nestjs/common&quot;;
import { GatewayTimeoutException, HttpException, Injectable } from &quot;@nestjs/common&quot;;
import { map, take } from &quot;rxjs&quot;;
import { EsResponseDto } from &quot;src/core/domain/dtos&quot;;
import { EsQueryDto } from &quot;src/core/domain/dtos/es-query.dto&quot;;
import { SearchResultDto } from &quot;src/core/domain/dtos/search-result.dto&quot;;
import { EsTime } from &quot;src/core/domain/enums/es-time.enum&quot;;
import { EsPit } from &quot;src/core/domain/interfaces/es-pit.interface&quot;;
import { EsResponseDto} from &quot;../../domain/dtos&quot;;
import { EsQueryDto } from &quot;../../domain/dtos/elastic/es-query.dto&quot;;
/**
* Search service provider
@ -398,12 +433,17 @@ export class SearchService {
*/
private readonly ES_PORT &#x3D; process.env.ES_PORT;
/**
* Elasticsearch IP address
*/
private readonly ES_IP &#x3D; process.env.ES_CONTAINER_NAME;
/**
* Finds a paper by its own ID
* @param uuid
* @returns Elasticsearch hits or an error object
*/
async findByID(uuid: string): Promise&lt;SearchResultDto&gt; { // Should I change &#x27;object&#x27; to specific DTO?
async findByID(uuid: string): Promise&lt;EsResponseDto&gt; { // Should I change &#x27;object&#x27; to specific DTO?
let ESQ: EsQueryDto &#x3D; new EsQueryDto;
ESQ.size &#x3D; 1;
@ -415,21 +455,19 @@ export class SearchService {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
(this.httpService.get&lt;EsResponseDto&gt;(&#x60;http://localhost:${this.ES_PORT}/_search&#x60;, {
(this.httpService.get&lt;EsResponseDto&gt;(&#x60;http://${this.ES_IP}:${this.ES_PORT}/_search&#x60;, {
data: ESQ,
headers: {&#x27;Content-Type&#x27;: &#x27;application/json&#x27;},
}))
.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
?.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
.subscribe((res: EsResponseDto) &#x3D;&gt; {
if (res.timed_out) {
throw new GatewayTimeoutException;
// reject(new SearchResultDto(504, {message: &#x27;Timed Out&#x27;}));
reject(new GatewayTimeoutException(&#x27;Elasticsearch Timed Out&#x27;));
}
resolve(new SearchResultDto(200, res));
resolve(res);
});
} catch (error) {
reject(new SearchResultDto(700, error));
reject(error);
}
});
}
@ -439,65 +477,27 @@ export class SearchService {
* @param query, &lt;EsQueryDto&gt;
* @returns Elasticsearch hits or an error object
*/
async findByContext(es_query: EsQueryDto): Promise&lt;SearchResultDto&gt; {
console.log(&#x60;SEARCH|SERVICE: ${JSON.stringify(es_query, null, 2)}&#x60;);
async findByContext(es_query: EsQueryDto): Promise&lt;EsResponseDto&gt; {
return new Promise((resolve, reject) &#x3D;&gt; {
try {
(this.httpService.get&lt;EsResponseDto&gt;(&#x60;http://localhost:${this.ES_PORT}/_search&#x60;, {
(this.httpService.get&lt;EsResponseDto&gt;(&#x60;http://${this.ES_IP}:${this.ES_PORT}/_search&#x60;, {
data: es_query,
headers: {&#x27;Content-Type&#x27;: &#x27;application/json&#x27;},
}))
.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
?.pipe(take(1), map(axiosRes &#x3D;&gt; axiosRes.data))
.subscribe((res: EsResponseDto) &#x3D;&gt; {
if (res.timed_out) {
throw new GatewayTimeoutException;
// reject(new SearchResultDto(504, {status: 504, message: &#x27;Timed Out&#x27;}));
reject(new GatewayTimeoutException(&#x27;Elasticsearch Timed Out&#x27;));
}
resolve(new SearchResultDto(200, res));
resolve(res);
});
} catch (error) {
reject(new SearchResultDto(700, error));
reject(error);
}
});
}
}
// let ESQ: EsQueryDto &#x3D; new EsQueryDto;
// if (limit) ESQ.size &#x3D; limit;
// ESQ.query &#x3D; {
// query_string: {
// query: query_str,
// default_field: &#x27;content&#x27;,
// }
// }
// this.getPIT(1).then((pit) &#x3D;&gt; {
// ESQ.pit &#x3D; pit;
// });
/**
* Context
* // let es_query &#x3D; { // DTO
// query: { // Interface
// query_string: { // Interface
// query: query_str,
// default_field: &quot;content&quot;
// }
// },
// }
*/
/**
* Single
* // let es_query &#x3D; {
// query: {
// query_string: {
// query: &#x27;id:&#x27; + uuid
// }
// },
// }
*/</code></pre>
}</code></pre>
</div>
</div>

View File

@ -66,7 +66,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/interfaces/es-query-string.interface.ts</code>
<code>src/core/domain/interfaces/elastic/es-query-string.interface.ts</code>
</p>
@ -273,11 +273,6 @@ Can&#39;t be specified with &#39;default_field&#39;</p>
* Can&#x27;t be specified with &#x27;default_field&#x27;
*/
fields?: string[];
/**
*
*/
}</code></pre>
</div>
</div>

View File

@ -66,7 +66,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/interfaces/es-pit.interface.ts</code>
<code>src/core/domain/interfaces/elastic/es-pit.interface.ts</code>
</p>

View File

@ -66,7 +66,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/interfaces/es-query.interface.ts</code>
<code>src/core/domain/interfaces/elastic/es-query.interface.ts</code>
</p>

View File

@ -66,7 +66,7 @@
<h3>File</h3>
</p>
<p class="comment">
<code>src/core/domain/interfaces/es-response-hits.interface.ts</code>
<code>src/core/domain/interfaces/elastic/es-response-hits.interface.ts</code>
</p>
@ -248,7 +248,7 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { EsHitDto } from &quot;../dtos/es-hit.dto&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { EsHitDto } from &quot;../../dtos/elastic/es-hit.dto&quot;;
/**
* Structure of &#x27;hits&#x27; object of Elasticsearch response

View File

@ -198,7 +198,7 @@ Indicates the starting point of next search</p>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { EsPit } from &quot;./es-pit.interface&quot;;
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { EsPit } from &quot;./elastic/es-pit.interface&quot;;
/**
* Structure of search metadata

View File

@ -121,13 +121,13 @@ customElements.define('compodoc-menu', class extends HTMLElement {
<a href="modules/SearchModule.html" data-type="entity-link" >SearchModule</a>
<li class="chapter inner">
<div class="simple menu-toggler" data-toggle="collapse" ${ isNormalMode ?
'data-target="#controllers-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' : 'data-target="#xs-controllers-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' }>
'data-target="#controllers-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' : 'data-target="#xs-controllers-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' }>
<span class="icon ion-md-swap"></span>
<span>Controllers</span>
<span class="icon ion-ios-arrow-down"></span>
</div>
<ul class="links collapse" ${ isNormalMode ? 'id="controllers-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' :
'id="xs-controllers-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' }>
<ul class="links collapse" ${ isNormalMode ? 'id="controllers-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' :
'id="xs-controllers-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' }>
<li class="link">
<a href="controllers/PapersController.html" data-type="entity-link" data-context="sub-entity" data-context-id="modules" >PapersController</a>
</li>
@ -135,13 +135,13 @@ customElements.define('compodoc-menu', class extends HTMLElement {
</li>
<li class="chapter inner">
<div class="simple menu-toggler" data-toggle="collapse" ${ isNormalMode ?
'data-target="#injectables-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' : 'data-target="#xs-injectables-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' }>
'data-target="#injectables-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' : 'data-target="#xs-injectables-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' }>
<span class="icon ion-md-arrow-round-down"></span>
<span>Injectables</span>
<span class="icon ion-ios-arrow-down"></span>
</div>
<ul class="links collapse" ${ isNormalMode ? 'id="injectables-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' :
'id="xs-injectables-links-module-SearchModule-97f6a3d41d8e000415549f58c31c09480db302f0be4dafb5300e2053a33b502290eaae74ae320340b5ed216dd9bb43790d919f5e4525ffd833604b101442c475"' }>
<ul class="links collapse" ${ isNormalMode ? 'id="injectables-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' :
'id="xs-injectables-links-module-SearchModule-8e897718c930e2d4958a2efea53e74e22c066b1dfa5983684b7710ac8d663ad84cf3b4a3f9d7db6ec2f207613ecc5cd2614e7ccaec013d1fb8cca1e2f728ae1a"' }>
<li class="link">
<a href="injectables/SearchService.html" data-type="entity-link" data-context="sub-entity" data-context-id="modules" >SearchService</a>
</li>
@ -189,6 +189,9 @@ customElements.define('compodoc-menu', class extends HTMLElement {
<li class="link">
<a href="classes/PageDto.html" data-type="entity-link" >PageDto</a>
</li>
<li class="link">
<a href="classes/PageMetaDto.html" data-type="entity-link" >PageMetaDto</a>
</li>
<li class="link">
<a href="classes/PaperDto.html" data-type="entity-link" >PaperDto</a>
</li>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -62,13 +62,7 @@
<td class="col-md-4">
<ul class="index-list">
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/es-hit.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-hit.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/es-query.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-query.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/es-response.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-response.dto.ts)</a>
<a href="#allowedProperties" title="src/core/domain/dtos/page-meta.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../page-meta.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/page.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../page.dto.ts)</a>
@ -85,6 +79,15 @@
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/search-result.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../search-result.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/elastic/es-hit.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-hit.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/elastic/es-query.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-query.dto.ts)</a>
</li>
<li>
<a href="#allowedProperties" title="src/core/domain/dtos/elastic/es-response.dto.ts" ><b>allowedProperties</b>&nbsp;&nbsp;&nbsp;(src/.../es-response.dto.ts)</a>
</li>
<li>
<a href="#configuration" title="src/infrastructure/config/env.objects.ts" ><b>configuration</b>&nbsp;&nbsp;&nbsp;(src/.../env.objects.ts)</a>
</li>
@ -110,7 +113,7 @@
</table>
</section>
<h3>src/core/domain/dtos/es-hit.dto.ts</h3>
<h3>src/core/domain/dtos/page-meta.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
@ -131,77 +134,7 @@
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;sort&#x27;, &#x27;_source&#x27;, &#x27;_score&#x27;]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>List of allowed properties in this DTO</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<h3>src/core/domain/dtos/es-query.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="allowedProperties"></a>
<span class="name">
<span ><b>allowedProperties</b></span>
<a href="#allowedProperties"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;size&#x27;, &#x27;query&#x27;, &#x27;pit&#x27;, &#x27;sort&#x27;]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>List of allowed properties in this DTO</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<h3>src/core/domain/dtos/es-response.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="allowedProperties"></a>
<span class="name">
<span ><b>allowedProperties</b></span>
<a href="#allowedProperties"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;took&#x27;, &#x27;timed_out&#x27;, &#x27;_shards&#x27;, &#x27;hits&#x27;, &#x27;pit_id&#x27;]</code>
<i>Default value : </i><code>[&#x27;total&#x27;, &#x27;pagenum&#x27;, &#x27;order&#x27;, &#x27;hasNext&#x27;, &#x27;hasPrev&#x27;, &#x27;pagesize&#x27;]</code>
</td>
</tr>
@ -390,6 +323,111 @@
</tbody>
</table>
</section>
<h3>src/core/domain/dtos/elastic/es-hit.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="allowedProperties"></a>
<span class="name">
<span ><b>allowedProperties</b></span>
<a href="#allowedProperties"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;sort&#x27;, &#x27;_source&#x27;, &#x27;_score&#x27;]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>List of allowed properties in this DTO</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<h3>src/core/domain/dtos/elastic/es-query.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="allowedProperties"></a>
<span class="name">
<span ><b>allowedProperties</b></span>
<a href="#allowedProperties"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;size&#x27;, &#x27;query&#x27;, &#x27;pit&#x27;, &#x27;sort&#x27;]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>List of allowed properties in this DTO</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<h3>src/core/domain/dtos/elastic/es-response.dto.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="allowedProperties"></a>
<span class="name">
<span ><b>allowedProperties</b></span>
<a href="#allowedProperties"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Default value : </i><code>[&#x27;took&#x27;, &#x27;timed_out&#x27;, &#x27;_shards&#x27;, &#x27;hits&#x27;, &#x27;pit_id&#x27;]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>List of allowed properties in this DTO</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<h3>src/infrastructure/config/env.objects.ts</h3>
<section>
<h3></h3> <table class="table table-sm table-bordered">

View File

@ -14,14 +14,14 @@
<title>cluster_CommonModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-32.8 8,-230.8 304,-230.8 304,-32.8 8,-32.8"/>
</g>
<g id="clust3" class="cluster">
<title>cluster_CommonModule_imports</title>
<polygon fill="none" stroke="black" points="20,-40.8 20,-92.8 292,-92.8 292,-40.8 20,-40.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_CommonModule_exports</title>
<polygon fill="none" stroke="black" points="16,-170.8 16,-222.8 296,-222.8 296,-170.8 16,-170.8"/>
</g>
<g id="clust3" class="cluster">
<title>cluster_CommonModule_imports</title>
<polygon fill="none" stroke="black" points="20,-40.8 20,-92.8 292,-92.8 292,-40.8 20,-40.8"/>
</g>
<!-- HttpResponseModule -->
<g id="node1" class="node">
<title>HttpResponseModule</title>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -57,14 +57,14 @@
<title>cluster_HttpResponseModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-32.8 8,-230.8 228,-230.8 228,-32.8 8,-32.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_HttpResponseModule_exports</title>
<polygon fill="none" stroke="black" points="40,-170.8 40,-222.8 196,-222.8 196,-170.8 40,-170.8"/>
</g>
<g id="clust6" class="cluster">
<title>cluster_HttpResponseModule_providers</title>
<polygon fill="none" stroke="black" points="16,-40.8 16,-92.8 220,-92.8 220,-40.8 16,-40.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_HttpResponseModule_exports</title>
<polygon fill="none" stroke="black" points="40,-170.8 40,-222.8 196,-222.8 196,-170.8 40,-170.8"/>
</g>
<!-- HttpResponseService -->
<g id="node1" class="node">
<title>HttpResponseService </title>

View File

@ -57,14 +57,14 @@
<title>cluster_LoggerModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-32.8 8,-230.8 176,-230.8 176,-32.8 8,-32.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_LoggerModule_exports</title>
<polygon fill="none" stroke="black" points="33,-170.8 33,-222.8 151,-222.8 151,-170.8 33,-170.8"/>
</g>
<g id="clust6" class="cluster">
<title>cluster_LoggerModule_providers</title>
<polygon fill="none" stroke="black" points="16,-40.8 16,-92.8 168,-92.8 168,-40.8 16,-40.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_LoggerModule_exports</title>
<polygon fill="none" stroke="black" points="33,-170.8 33,-222.8 151,-222.8 151,-170.8 33,-170.8"/>
</g>
<!-- LoggerService -->
<g id="node1" class="node">
<title>LoggerService </title>

View File

@ -14,14 +14,14 @@
<title>cluster_LoggerModule</title>
<polygon fill="none" stroke="black" stroke-dasharray="1,5" points="8,-32.8 8,-230.8 176,-230.8 176,-32.8 8,-32.8"/>
</g>
<g id="clust6" class="cluster">
<title>cluster_LoggerModule_providers</title>
<polygon fill="none" stroke="black" points="16,-40.8 16,-92.8 168,-92.8 168,-40.8 16,-40.8"/>
</g>
<g id="clust4" class="cluster">
<title>cluster_LoggerModule_exports</title>
<polygon fill="none" stroke="black" points="33,-170.8 33,-222.8 151,-222.8 151,-170.8 33,-170.8"/>
</g>
<g id="clust6" class="cluster">
<title>cluster_LoggerModule_providers</title>
<polygon fill="none" stroke="black" points="16,-40.8 16,-92.8 168,-92.8 168,-40.8 16,-40.8"/>
</g>
<!-- LoggerService -->
<g id="node1" class="node">
<title>LoggerService </title>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -134,7 +134,7 @@
<h3>Description</h3>
</p>
<p class="comment">
<p>search module</p>
<p>Search module</p>
</p>
@ -178,11 +178,11 @@
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { HttpModule } from &quot;@nestjs/axios&quot;;
import { Module } from &quot;@nestjs/common&quot;;
import { PapersController } from &quot;src/application&quot;;
import { PapersController } from &quot;../../application&quot;;
import { SearchService } from &quot;../../core/services/common/search.service&quot;;
/**
* search module
* Search module
*/
@Module({
imports: [

View File

@ -301,7 +301,7 @@
<div class="card text-center">
<div class="card-block">
<h4 class="card-title"><span class="icon ion-ios-paper"></span></h4>
<p class="card-text">11 Classes</p>
<p class="card-text">12 Classes</p>
</div>
</div>
</div>

20
package-lock.json generated
View File

@ -4058,7 +4058,8 @@
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true
},
"node_modules/ast-transform": {
"version": "0.0.0",
@ -5302,6 +5303,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
"integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==",
"dev": true,
"dependencies": {
"asap": "^2.0.0",
"wrappy": "1"
@ -7047,6 +7049,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"engines": {
"node": ">=0.8.19"
}
@ -12861,7 +12864,8 @@
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"node_modules/throat": {
"version": "6.0.1",
@ -13695,6 +13699,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz",
"integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==",
"dev": true,
"dependencies": {
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.7"
@ -16733,7 +16738,8 @@
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true
},
"ast-transform": {
"version": "0.0.0",
@ -17692,6 +17698,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
"integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==",
"dev": true,
"requires": {
"asap": "^2.0.0",
"wrappy": "1"
@ -18984,7 +18991,8 @@
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true
},
"inflight": {
"version": "1.0.6",
@ -23388,7 +23396,8 @@
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"throat": {
"version": "6.0.1",
@ -23985,6 +23994,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz",
"integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==",
"dev": true,
"requires": {
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.7"

View File

@ -19,7 +19,7 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"test:e2e": "jest --config ./src/test/jest-e2e.json",
"doc": "./node_modules/.bin/compodoc -p tsconfig.json -w -s -r 7000 --theme 'readthedocs'"
},
"dependencies": {
@ -89,6 +89,11 @@
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coveragePathIgnorePatterns": [
"<rootDir>/dist/",
"<rootDir>/documentation",
"<rootDir>/.eslintrc.js"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}

View File

@ -1,9 +1,9 @@
import { Controller, Get, HttpCode, HttpException, Next, Param, ParseUUIDPipe, Put, Query, Req, Res, UseInterceptors } from "@nestjs/common";
import { Controller, Get, HttpCode, Param, ParseUUIDPipe, Req, UseInterceptors } from "@nestjs/common";
import { SearchService } from "../../core/services/common/search.service";
import { PageInterceptor } from "src/core/interceptors/page.interceptor";
import { SearchResultDto } from "src/core/domain/dtos/search-result.dto";
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
import { RequestDto } from "src/core/domain/dtos/request.dto";
import { PageInterceptor } from "../../core/interceptors/page.interceptor";
import { ApiExtraModels, ApiGatewayTimeoutResponse, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from "@nestjs/swagger";
import { RequestDto } from "../../core/domain/dtos/request.dto";
import { EsHitDto, EsResponseDto, PageDto, PaperDto } from "../../core/domain";
/**
* /papers/ route controller
@ -12,6 +12,7 @@ import { RequestDto } from "src/core/domain/dtos/request.dto";
version: '1',
path: 'papers',
})
@ApiExtraModels(RequestDto, EsHitDto, EsResponseDto)
export class PapersController {
constructor(private searchService: SearchService) {}
@ -21,22 +22,28 @@ export class PapersController {
* @param response
* @returns a response with a set of matching papers
*/
@ApiOperation({ summary: 'Finds papers by context based on the query.' })
@ApiResponse({
status: 200,
description: 'Returns back acquired papers.',
type: SearchResultDto,
})
@ApiTags('Search')
@ApiOperation({
summary: 'Finds papers by context based on the query',
})
@ApiResponse({
status: 200,
description: 'Returns back a page with acquired papers',
type: PageDto
})
@ApiGatewayTimeoutResponse({
description: 'Elasticsearch request timed out'
})
@Get('search')
@UseInterceptors(PageInterceptor)
@HttpCode(200)
getByContext(@Req() query: RequestDto): object {
return this.searchService.findByContext(query.es_query).then(
(response: SearchResultDto) => {
return response.data;
getByContext(@Req() request: RequestDto): Promise<EsResponseDto> {
return this.searchService.findByContext(request.es_query).then(
(response) => {
return response;
},
(error: SearchResultDto) => {
throw new HttpException(error.data, error.statusCode);
(error) => {
throw error;
}
);
}
@ -47,22 +54,28 @@ export class PapersController {
* @param response
* @returns a response with a requested object
*/
@ApiOperation({ summary: 'Finds paper by its UUID.' })
@ApiResponse({
status: 200,
description: 'Returns back acquired paper.',
type: SearchResultDto,
})
@ApiTags('Search')
@ApiOperation({
summary: 'Finds paper by its UUID',
tags: ['Search']
})
@ApiResponse({
status: 200,
description: 'Returns back a paper',
type: PaperDto
})
@ApiGatewayTimeoutResponse({
description: 'Elasticsearch request timed out'
})
@Get(':uuid')
@UseInterceptors(PageInterceptor)
@HttpCode(200)
getByID(@Param('uuid', ParseUUIDPipe) uuid: string): object {
getByID(@Param('uuid', ParseUUIDPipe) uuid: string): Promise<PaperDto> {
return this.searchService.findByID(uuid).then(
(response) => {
return response.data;
(response: EsResponseDto) => {
return response.hits.hits[0]._source;
},
(error) => {
throw new HttpException(error.data, error.status);
throw error;
}
);
}

View File

@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsArray, IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { PaperDto } from "./paper.dto";
import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsNotEmpty, IsOptional } from "class-validator";
import { PaperDto } from "../paper.dto";
/**
* List of allowed properties in this DTO
@ -10,6 +10,7 @@ const allowedProperties = ['sort', '_source', '_score'];
/**
* Structure of the document stored and retrieved from Elasticsearch
*/
@ApiExtraModels()
export class EsHitDto {
/**
* Actual document stored in Elasticsearch
@ -27,7 +28,7 @@ export class EsHitDto {
* List of objects that represents how the hit was sorted
*/
@IsOptional()
@ApiProperty({
@ApiPropertyOptional({
description: 'List of objects that represents how the hit was sorted',
example: {}
})
@ -37,7 +38,7 @@ export class EsHitDto {
* Hit relevance score
*/
@IsOptional()
@ApiProperty({
@ApiPropertyOptional({
description: 'Relevance score',
example: 1.2355
})

View File

@ -1,7 +1,7 @@
import { ApiProperty } from "@nestjs/swagger";
import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsArray, IsDefined, IsInt, IsNotEmpty, IsNumber, IsObject, IsOptional } from "class-validator";
import { EsPit } from "../interfaces/es-pit.interface";
import { EsQuery } from "../interfaces/es-query.interface"
import { EsPit } from "../../interfaces/elastic/es-pit.interface";
import { EsQuery } from "../../interfaces/elastic/es-query.interface"
/**
* List of allowed properties in this DTO
@ -11,6 +11,7 @@ import { EsQuery } from "../interfaces/es-query.interface"
/**
* Elasticsearch query DTO
*/
@ApiExtraModels()
export class EsQueryDto {
/**
* Maximum number of elements returned by Elasticsearch
@ -19,7 +20,7 @@ import { EsQuery } from "../interfaces/es-query.interface"
@IsDefined()
@IsNumber()
@IsInt()
@ApiProperty({
@ApiPropertyOptional({
description: 'Maximum number of elements returned by Elasticsearch',
example: 30
})
@ -41,7 +42,7 @@ import { EsQuery } from "../interfaces/es-query.interface"
*/
@IsOptional()
@IsObject()
@ApiProperty({
@ApiPropertyOptional({
description: 'PIT object',
example: {}
})
@ -52,7 +53,7 @@ import { EsQuery } from "../interfaces/es-query.interface"
*/
@IsOptional()
@IsArray()
@ApiProperty({
@ApiPropertyOptional({
description: '',
example: []
})
@ -63,7 +64,7 @@ import { EsQuery } from "../interfaces/es-query.interface"
*/
@IsOptional()
@IsArray()
@ApiProperty({
@ApiPropertyOptional({
description: '',
example: []
})

View File

@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsBoolean, IsDefined, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from "class-validator";
import { EsResponseHits } from "../interfaces/es-response-hits.interface";
import { EsResponseHits } from "../../interfaces/elastic/es-response-hits.interface";
/**
* List of allowed properties in this DTO
@ -10,6 +10,7 @@ const allowedProperties = ['took', 'timed_out', '_shards', 'hits', 'pit_id'];
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class EsResponseDto {
/**
* Number of milliseconds it
@ -19,7 +20,7 @@ export class EsResponseDto {
@IsNotEmpty()
@IsNumber()
@ApiProperty({
description: 'took',
description: 'The time that it took Elasticsearch to process the query',
example: 5
})
took: number;
@ -32,7 +33,7 @@ export class EsResponseDto {
@IsNotEmpty()
@IsBoolean()
@ApiProperty({
description: 'timed_out',
description: 'Shows if request timed out before completion',
example: false,
})
timed_out: boolean;
@ -44,7 +45,7 @@ export class EsResponseDto {
@IsOptional()
@IsObject()
@ApiProperty({
description: '_shards',
description: 'Contains a count of Elasticsearch shards used to process the request',
example: {
total: 1,
successful: 1,
@ -60,7 +61,7 @@ export class EsResponseDto {
@IsOptional()
@IsObject()
@ApiProperty({
description: 'hits',
description: 'Contains returned documents and metadata',
example: {
total: {
value: 3,
@ -71,12 +72,8 @@ export class EsResponseDto {
_index: 'papers',
_id: '01002',
_score: 1.2,
_source: {
},
fields: {
}
_source: {},
fields: {}
}],
}
})
@ -87,8 +84,8 @@ export class EsResponseDto {
*/
@IsString()
@IsOptional()
@ApiProperty({
description: 'PIT ID used to search for results',
@ApiPropertyOptional({
description: 'Contains PIT ID used to search for results',
example: '46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=='
})
pit_id?: string;

View File

@ -1,3 +1,8 @@
export * from './es-response.dto'
export * from './page.dto'
export * from './search-q.dto'
export * from './elastic/es-query.dto';
export * from './elastic/es-response.dto';
export * from './elastic/es-hit.dto';
export * from './page.dto';
export * from './search-q.dto';
export * from './search-result.dto';
export * from './paper.dto';
export * from './request.dto';

View File

@ -0,0 +1,73 @@
import { ApiExtraModels, ApiProperty, PartialType } from "@nestjs/swagger";
import { IsArray } from "class-validator";
import { Order } from "../enums";
import { PageMeta } from "../interfaces/page-meta.interface";
import { PaperDto } from "./paper.dto";
/**
* List of allowed properties in this DTO
*/
const allowedProperties = ['total', 'pagenum', 'order', 'hasNext', 'hasPrev', 'pagesize'];
/**
* Page model for pagination
*/
@ApiExtraModels()
export class PageMetaDto implements PageMeta {
/**
* Total number of hits (results) acquired from the search
*/
@IsArray()
@ApiProperty({
description: 'Total number of hits (results) acquired from the search',
example: 314
})
total: number;
/**
* Current page number
*/
@ApiProperty({
description: 'Current page number',
minimum: 1,
example: 3
})
pagenum: number;
/**
* Order of the elements on the page
*/
@ApiProperty({
description: 'Order of the elements on the page',
example: Order.DESC
})
order: Order;
/**
* Flag, that shows if there's a page following the current one
*/
@ApiProperty({
description: 'Flag, that shows if there\'s a page following the current one',
example: true
})
hasNext: boolean;
/**
* Flag, that shows if there's a page preceding the current one
*/
@ApiProperty({
description: 'Flag, that shows if there\'s a page preceding the current one',
example: true
})
hasPrev: boolean;
/**
* Maximum number of elements on the page
*/
@ApiProperty({
description: 'Maximum number of elements on the page',
minimum: 1,
example: 20
})
pagesize: number;
}

View File

@ -1,6 +1,8 @@
import { ApiProperty } from "@nestjs/swagger";
import { ApiExtraModels, ApiProperty, PartialType } from "@nestjs/swagger";
import { IsArray } from "class-validator";
import { Order } from "../enums";
import { PageMeta } from "../interfaces/page-meta.interface";
import { PageMetaDto } from "./page-meta.dto";
import { PaperDto } from "./paper.dto";
/**
@ -11,14 +13,16 @@ const allowedProperties = ['data', 'meta'];
/**
* Page model for pagination
*/
@ApiExtraModels()
export class PageDto {
/**
* Data block of the page
*/
@IsArray()
@ApiProperty({
description: 'All data the page contains',
description: 'All data (papers) the page contains',
isArray: true,
type: PaperDto
})
readonly data: PaperDto[];
@ -27,9 +31,10 @@ export class PageDto {
*/
@ApiProperty({
description: 'Metadata for the page',
// example: [],
// example: {},
})
readonly meta: PageMeta;
readonly meta: PageMetaDto;
/**
* Constructs an object with provided parameters

View File

@ -1,7 +1,5 @@
import { ApiProperty } from "@nestjs/swagger";
import { ApiExtraModels, ApiProperty } from "@nestjs/swagger";
import { IsArray, IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { EsQueryDto } from "./es-query.dto";
import { SearchQueryDto } from "./search-q.dto";
/**
* List of allowed properties in this DTO
@ -11,6 +9,7 @@ const allowedProperties = ['id', 'title', 'authors', 'topic', 'summary', 'tags',
/**
* Structure of the document stored and retrieved from Elasticsearch
*/
@ApiExtraModels()
export class PaperDto {
/**
* Unique ID of the paper

View File

@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { EsQueryDto } from "./es-query.dto";
import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsDefined, IsNotEmpty, IsOptional } from "class-validator";
import { EsQueryDto } from "./elastic/es-query.dto";
import { SearchQueryDto } from "./search-q.dto";
/**
@ -11,6 +11,7 @@ const allowedProperties = ['query', 'es_query'];
/**
* Request object, which contains query parameters and Elasticsearch query object
*/
@ApiExtraModels()
export class RequestDto {
/**
* Query parameters object
@ -18,7 +19,8 @@ export class RequestDto {
@IsDefined()
@IsNotEmpty()
@ApiProperty({
description: '',
type: SearchQueryDto,
description: 'Actual query with parameters acquired from the request',
example: {}
})
query: SearchQueryDto;
@ -27,8 +29,9 @@ export class RequestDto {
* Elasticsearch query object
*/
@IsOptional()
@ApiProperty({
description: '',
@ApiPropertyOptional({
type: EsQueryDto,
description: 'Elasticsearch query body constructed by pagination mechanism',
example: {},
})
es_query?: EsQueryDto;
@ -38,8 +41,8 @@ export class RequestDto {
* @param query
* @param es_query
*/
constructor(query: SearchQueryDto, es_query: EsQueryDto) {
constructor(query: SearchQueryDto, es_query: EsQueryDto) {
this.query = query;
this.es_query = es_query;
}
}
}

View File

@ -1,5 +1,5 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { ApiExtraModels, ApiProperty } from "@nestjs/swagger";
import { IsDefined, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
/**
* List of allowed properties in this DTO
@ -9,6 +9,7 @@ const allowedProperties = ['query', 'pagen', 'limit', 'order'];
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class SearchQueryDto {
/**
* Given query string to perform the

View File

@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsArray, IsDefined, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { EsResponseDto } from "./es-response.dto";
import { ApiExtraModels, ApiProperty } from "@nestjs/swagger";
import { IsArray, IsDefined, IsInt, IsNotEmpty } from "class-validator";
import { EsResponseDto } from "./elastic/es-response.dto";
/**
* List of allowed properties in this DTO
@ -10,6 +10,7 @@ const allowedProperties = ['data', 'status'];
/**
* Elasticsearch response DTO
*/
@ApiExtraModels()
export class SearchResultDto {
/**
* Status code
@ -32,7 +33,10 @@ export class SearchResultDto {
@ApiProperty({
description: 'Data acquired from the Elasticsearch',
example: {
took: 1,
timed_out: false,
_shards: {},
hits: {}
},
})
data: EsResponseDto;

View File

@ -2,11 +2,38 @@
* Elasticsearch time-units
*/
export enum EsTime {
/**
* Days
*/
days = 'd',
/**
* Hours
*/
hours = 'h',
/**
* Minutes
*/
min = 'm',
/**
* Seconds
*/
sec = 's',
/**
* Milliseconds
*/
ms = 'ms',
/**
* Microseconds
*/
us = 'micros',
/**
* Nanoseconds
*/
ns = 'nanos'
}

View File

@ -1,3 +1,4 @@
export * from './httpResponse'
export * from './roles.enum'
export * from './page-order.enum'
export * from './page-order.enum'
export * from './es-time.enum'

View File

@ -2,6 +2,13 @@
* Page display order
*/
export enum Order {
/**
* Ascending order
*/
ASC = 'asc',
/**
* Descending order
*/
DESC = 'desc',
}

View File

@ -1 +1,3 @@
export * from './enums'
export * from './enums'
export * from './dtos'
export * from './interfaces'

View File

@ -18,9 +18,4 @@
* Can't be specified with 'default_field'
*/
fields?: string[];
/**
*
*/
}

View File

@ -1,4 +1,4 @@
import { EsHitDto } from "../dtos/es-hit.dto";
import { EsHitDto } from "../../dtos/elastic/es-hit.dto";
/**
* Structure of 'hits' object of Elasticsearch response

View File

@ -1,4 +1,7 @@
export * from './http-response.interface';
export * from './http-response.interface'
export * from './page-meta.interface'
export * from './es-query.interface'
export * from './es-query-string.interface'
export * from './search-info.interface'
export * from './elastic/es-query.interface'
export * from './elastic/es-query-string.interface'
export * from './elastic/es-response-hits.interface'
export * from './elastic/es-pit.interface'

View File

@ -1,4 +1,4 @@
import { EsPit } from "./es-pit.interface";
import { EsPit } from "./elastic/es-pit.interface";
/**
* Structure of search metadata

View File

@ -2,20 +2,18 @@ import { HttpService } from "@nestjs/axios";
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
import { Observable, map, take } from "rxjs";
import { PageDto } from "../domain/dtos";
import { EsHitDto } from "../domain/dtos/es-hit.dto";
import { EsQueryDto } from "../domain/dtos/es-query.dto";
import { EsQueryDto } from "../domain/dtos/elastic/es-query.dto";
import { RequestDto } from "../domain/dtos/request.dto";
import { SearchQueryDto } from "../domain/dtos/search-q.dto";
import { EsTime } from "../domain/enums/es-time.enum";
import { Order } from "../domain/enums/page-order.enum";
import { PageMeta } from "../domain/interfaces";
import { EsPit } from "../domain/interfaces/es-pit.interface";
import { SearchInfo } from "../domain/interfaces/search-info.interface";
import { EsPit } from "../domain/interfaces/elastic/es-pit.interface";
/**
* Previous search data storage
*/
class PrevSearch implements SearchInfo {
class PrevSearch {
/**
* Constructs an uninitialized object
*/
@ -28,17 +26,35 @@ class PrevSearch implements SearchInfo {
/**
* PIT object of the previous search
*/
pit: EsPit;
private pit: EsPit;
set _pit(pit: EsPit) {
this.pit = pit;
}
get _pit(): EsPit {
return this.pit;
}
/**
* Tiebreaker and sort parameters
*/
tiebreaker: unknown[];
private tiebreaker: unknown[];
set _tiebreaker(tiebreaker: unknown[]) {
this.tiebreaker = tiebreaker;
}
get _tiebreaker(): unknown[] {
return this.tiebreaker;
}
/**
* Number of the previous page
*/
prevPage: number;
private prevPage: number;
set _prevPage(page: number) {
this.prevPage = page;
}
get _prevPage(): number {
return this.prevPage;
}
/**
* Checks if there was the search before current one
@ -89,21 +105,23 @@ export class PageInterceptor implements NestInterceptor {
];
if (this.prevSearch.isSet()) {
request.es_query.pit = this.prevSearch.pit;
request.es_query.search_after = this.prevSearch.tiebreaker;
request.es_query.pit = this.prevSearch._pit;
request.es_query.search_after = this.prevSearch._tiebreaker;
let limit = !query?.limit ? 10 : query.limit;
request.es_query.size = limit * Math.abs(query.page - this.prevSearch.prevPage);
request.es_query.size = limit * Math.abs(query.page - this.prevSearch._prevPage);
if (query.page < this.prevSearch.prevPage) {
if (query.page < this.prevSearch._prevPage) {
request.es_query.sort = [{ _score: { order: 'asc' } }];
request.es_query.size += limit - 1;
reverse = true;
} else if (query.page == this.prevSearch.prevPage) {
//...
} else if (query.page == this.prevSearch._prevPage) {
// Caching should be HERE
request.es_query.sort = [{ _score: { order: 'asc' } }];
reverse = true;
}
} else {
this.prevSearch.pit = request.es_query.pit = await this.getPIT(1);
this.prevSearch._pit = request.es_query.pit = await this.getPIT(1);
let limit = !query?.limit ? 10 : query.limit;
request.es_query.size = limit * query.page;
@ -114,24 +132,24 @@ export class PageInterceptor implements NestInterceptor {
// Setting the page meta-data
let meta: PageMeta = {
total: res.hits.total.value,
pagenum: !query?.page ? 1 : query.page,
pagenum: !query?.page ? 1 : +query.page,
order: query?.order?.toUpperCase() === Order.ASC ? Order.ASC : Order.DESC,
pagesize: !query?.limit ? 10 : query.limit,
hasNext: undefined,
hasPrev: undefined,
pagesize: !query?.limit ? 10 : query.limit,
};
meta.hasNext = meta.pagenum * meta.pagesize < meta.total ? true : false;
meta.hasPrev = meta.pagenum != 1 ? true : false;
// Saving the search info
this.prevSearch.pit.id = res.pit_id;
this.prevSearch.tiebreaker = res.hits.hits[res.hits.hits.length - 1]?.sort;
this.prevSearch.prevPage = query.page;
this.prevSearch._pit.id = res.pit_id;
this.prevSearch._tiebreaker = res.hits.hits[res.hits.hits.length - 1]?.sort;
this.prevSearch._prevPage = query.page;
// Check if the performed search is a backward search
// Check if the performed search is a backwards search
let data = res.hits.hits.slice(-meta.pagesize);
if (reverse) {
this.prevSearch.tiebreaker = data[0]?.sort;
this.prevSearch._tiebreaker = data[0]?.sort;
data.reverse();
reverse = false;
}
@ -150,6 +168,11 @@ export class PageInterceptor implements NestInterceptor {
*/
private readonly ES_PORT = process.env.ES_PORT;
/**
* Elastichsearch IP address
*/
private readonly ES_IP = process.env.ES_CONTAINER_NAME;
/**
* Info about previously completed search
*/
@ -163,12 +186,12 @@ export class PageInterceptor implements NestInterceptor {
public async getPIT(alive: number, unit: EsTime = EsTime.min): Promise<EsPit> {
return new Promise((resolve, reject) => {
try {
(this.httpService.post<EsPit>(`http://localhost:${this.ES_PORT}/papers/_pit?keep_alive=${alive+unit}`)
this.httpService.post<EsPit>(`http://${this.ES_IP}:${this.ES_PORT}/papers/_pit?keep_alive=${alive+unit}`)
.pipe(take(1), map(axiosRes => axiosRes.data))
.subscribe((res) => {
.subscribe((res: EsPit) => {
res.keep_alive = alive + unit;
resolve(res);
}));
});
} catch (error) {
reject(error);
}
@ -183,7 +206,7 @@ export class PageInterceptor implements NestInterceptor {
async deletePIT(pitID: string): Promise<boolean> {
return new Promise((resolve, reject) => {
try {
this.httpService.delete(`http://localhost:${this.ES_PORT}/_pit`, {
this.httpService.delete(`http://${this.ES_IP}:${this.ES_PORT}/_pit`, {
data: { id: pitID },
headers: { 'Content-Type': 'application/json' },
})

View File

@ -1,11 +1,8 @@
import { HttpService } from "@nestjs/axios";
import { GatewayTimeoutException, Injectable } from "@nestjs/common";
import { GatewayTimeoutException, HttpException, Injectable } from "@nestjs/common";
import { map, take } from "rxjs";
import { EsResponseDto } from "src/core/domain/dtos";
import { EsQueryDto } from "src/core/domain/dtos/es-query.dto";
import { SearchResultDto } from "src/core/domain/dtos/search-result.dto";
import { EsTime } from "src/core/domain/enums/es-time.enum";
import { EsPit } from "src/core/domain/interfaces/es-pit.interface";
import { EsResponseDto} from "../../domain/dtos";
import { EsQueryDto } from "../../domain/dtos/elastic/es-query.dto";
/**
* Search service provider
@ -24,12 +21,17 @@ export class SearchService {
*/
private readonly ES_PORT = process.env.ES_PORT;
/**
* Elasticsearch IP address
*/
private readonly ES_IP = process.env.ES_CONTAINER_NAME;
/**
* Finds a paper by its own ID
* @param uuid
* @returns Elasticsearch hits or an error object
*/
async findByID(uuid: string): Promise<SearchResultDto> { // Should I change 'object' to specific DTO?
async findByID(uuid: string): Promise<EsResponseDto> { // Should I change 'object' to specific DTO?
let ESQ: EsQueryDto = new EsQueryDto;
ESQ.size = 1;
@ -41,21 +43,19 @@ export class SearchService {
return new Promise((resolve, reject) => {
try {
(this.httpService.get<EsResponseDto>(`http://localhost:${this.ES_PORT}/_search`, {
(this.httpService.get<EsResponseDto>(`http://${this.ES_IP}:${this.ES_PORT}/_search`, {
data: ESQ,
headers: {'Content-Type': 'application/json'},
}))
.pipe(take(1), map(axiosRes => axiosRes.data))
?.pipe(take(1), map(axiosRes => axiosRes.data))
.subscribe((res: EsResponseDto) => {
if (res.timed_out) {
throw new GatewayTimeoutException;
// reject(new SearchResultDto(504, {message: 'Timed Out'}));
reject(new GatewayTimeoutException('Elasticsearch Timed Out'));
}
resolve(new SearchResultDto(200, res));
resolve(res);
});
} catch (error) {
reject(new SearchResultDto(700, error));
reject(error);
}
});
}
@ -65,62 +65,24 @@ export class SearchService {
* @param query, <EsQueryDto>
* @returns Elasticsearch hits or an error object
*/
async findByContext(es_query: EsQueryDto): Promise<SearchResultDto> {
console.log(`SEARCH|SERVICE: ${JSON.stringify(es_query, null, 2)}`);
async findByContext(es_query: EsQueryDto): Promise<EsResponseDto> {
return new Promise((resolve, reject) => {
try {
(this.httpService.get<EsResponseDto>(`http://localhost:${this.ES_PORT}/_search`, {
(this.httpService.get<EsResponseDto>(`http://${this.ES_IP}:${this.ES_PORT}/_search`, {
data: es_query,
headers: {'Content-Type': 'application/json'},
}))
.pipe(take(1), map(axiosRes => axiosRes.data))
?.pipe(take(1), map(axiosRes => axiosRes.data))
.subscribe((res: EsResponseDto) => {
if (res.timed_out) {
throw new GatewayTimeoutException;
// reject(new SearchResultDto(504, {status: 504, message: 'Timed Out'}));
reject(new GatewayTimeoutException('Elasticsearch Timed Out'));
}
resolve(new SearchResultDto(200, res));
resolve(res);
});
} catch (error) {
reject(new SearchResultDto(700, error));
reject(error);
}
});
}
}
// let ESQ: EsQueryDto = new EsQueryDto;
// if (limit) ESQ.size = limit;
// ESQ.query = {
// query_string: {
// query: query_str,
// default_field: 'content',
// }
// }
// this.getPIT(1).then((pit) => {
// ESQ.pit = pit;
// });
/**
* Context
* // let es_query = { // DTO
// query: { // Interface
// query_string: { // Interface
// query: query_str,
// default_field: "content"
// }
// },
// }
*/
/**
* Single
* // let es_query = {
// query: {
// query_string: {
// query: 'id:' + uuid
// }
// },
// }
*/
}

View File

@ -1,5 +1,5 @@
import { plainToClass } from 'class-transformer';
import { validateSync, IsOptional } from 'class-validator';
import { validateSync } from 'class-validator';
/**
* env vatiables

View File

@ -1 +1,2 @@
export * from './app.module';
export * from './search.module'

View File

@ -1,10 +1,10 @@
import { HttpModule } from "@nestjs/axios";
import { Module } from "@nestjs/common";
import { PapersController } from "src/application";
import { PapersController } from "../../application";
import { SearchService } from "../../core/services/common/search.service";
/**
* search module
* Search module
*/
@Module({
imports: [

View File

@ -19,6 +19,9 @@ async function bootstrap() {
})
);
/**
* Enabling URI-type versioning of the API
*/
app.enableVersioning({
type: VersioningType.URI,
});
@ -27,12 +30,14 @@ async function bootstrap() {
* Configuration of the Swagger document
*/
const config = new DocumentBuilder()
.setTitle('Nestjs boilerplate')
.setDescription('This is a nest clean architecture boilerplate')
.setTitle('Freeland')
.setDescription('Freeland open library API')
.setVersion('0.0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
const document = SwaggerModule.createDocument(app, config, {
deepScanRoutes: true,
});
SwaggerModule.setup('api', app, document);
try {

View File

@ -0,0 +1,227 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import request from 'supertest'
import { AppModule } from "../../infrastructure/modules";
import { ConfigModule } from "@nestjs/config";
import { HttpService } from "@nestjs/axios";
import { of } from "rxjs";
import { Order } from "../../core/domain";
describe('E2E Testing of /papers', () => {
let app: INestApplication;
let httpService: HttpService;
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [
AppModule,
ConfigModule.forRoot({
isGlobal: true,
cache: true,
expandVariables: true
})
],
providers: [
{
provide: HttpService,
useValue: {
get: jest.fn(),
post: jest.fn(),
delete: jest.fn()
}
}
]
}).compile();
app = moduleRef.createNestApplication();
httpService = moduleRef.get(HttpService)
await app.init();
});
it('Should be defined', () => {
expect(app).toBeDefined();
expect(httpService).toBeDefined();
});
it('GET /papers/{uuid} | Should return one exact item', async () => {
const axiosRes = of({
status: 200,
statusText: 'statText',
headers: null,
config: null,
data: {
took: 5,
timed_out: false,
_shards: {},
hits: {
total: {},
hits: [
{
_source: {
id: 'thisIsIDofTheFirstObject',
title: 'thisIsTitle',
authors: ['A1', 'A2', 'A3'],
topic: 'thisIsTopic',
summary: 'thisIsSummary',
tags: ['T1', 'T2', 'T3'],
content: 'thisIsContent'
}
}
]
}
}
});
let httpGetSpy = jest.spyOn(httpService, 'get').mockReturnValueOnce(axiosRes);
const test = await request(app.getHttpServer())
.get('/papers/2d3dc418-7778-abab-b33f-3d63aa25db41') // ??? Fetch a random object from DB
.expect(200);
// Expect HttpService.get() method to be touched
expect(httpGetSpy).toHaveBeenCalled();
expect(httpGetSpy).toHaveReturnedWith(axiosRes);
// Checking received data
expect(test.body).toBeDefined();
expect(test.body.id).toBeDefined();
expect(test.body.id).toBe('thisIsIDofTheFirstObject');
expect(test.body.title).toBeDefined();
expect(test.body.title).toBe('thisIsTitle');
expect(test.body.authors).toBeDefined();
expect(test.body.authors).toEqual(['A1', 'A2', 'A3']);
expect(test.body.topic).toBeDefined();
expect(test.body.topic).toBe('thisIsTopic');
expect(test.body.summary).toBeDefined();
expect(test.body.summary).toBe('thisIsSummary');
expect(test.body.tags).toBeDefined();
expect(test.body.tags).toEqual(['T1', 'T2', 'T3']);
expect(test.body.content).toBeDefined();
expect(test.body.content).toBe('thisIsContent');
});
it('GET /papers/search? | Should return multiple items on the page and correct meta', async () => {
const axiosResGet = of({
status: 200,
statusText: 'statText',
headers: null,
config: null,
data: {
took: 5,
timed_out: false,
_shards: {},
hits: {
total: {
value: 2,
},
hits: [
{
_source: {
id: 'thisIsIDofTheFirstObject',
title: 'thisIsTitle1',
authors: ['A1', 'A2', 'A3'],
topic: 'thisIsTopic1',
summary: 'thisIsSummary1',
tags: ['T1', 'T2', 'T3'],
content: 'thisIsContent1'
}
},
{
_source: {
id: 'thisIsIDofTheSecondObject',
title: 'thisIsTitle2',
authors: ['A4', 'A5', 'A6'],
topic: 'thisIsTopic2',
summary: 'thisIsSummary2',
tags: ['T11', 'T2', 'T8'],
content: 'thisIsContent2'
}
}
]
}
}
});
let httpGetSpy = jest.spyOn(httpService, 'get').mockReturnValueOnce(axiosResGet);
let httpPostSpy = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({
data: {},
status: 200,
statusText: 'statText',
headers: null,
config: null,
}));
const test = await request(app.getHttpServer())
.get('/papers/search?query=at&page=1')
.expect(200);
// Expect HttpService.get() method to be touched
expect(httpGetSpy).toHaveBeenCalled();
expect(httpGetSpy).toHaveReturnedWith(axiosResGet);
// Checking received data
expect(test.body.data).toBeDefined();
expect(test.body.data.length).toBe(2);
for (const paper of test.body.data) {
expect(paper.id).toBeDefined();
expect(paper.title).toBeDefined();
expect(paper.authors).toBeDefined();
expect(paper.topic).toBeDefined();
expect(paper.summary).toBeDefined();
expect(paper.tags).toBeDefined();
expect(paper.content).toBeDefined();
}
expect(test.body.data[0]).toEqual({
id: 'thisIsIDofTheFirstObject',
title: 'thisIsTitle1',
authors: ['A1', 'A2', 'A3'],
topic: 'thisIsTopic1',
summary: 'thisIsSummary1',
tags: ['T1', 'T2', 'T3'],
content: 'thisIsContent1'
});
expect(test.body.data[1]).toEqual({
id: 'thisIsIDofTheSecondObject',
title: 'thisIsTitle2',
authors: ['A4', 'A5', 'A6'],
topic: 'thisIsTopic2',
summary: 'thisIsSummary2',
tags: ['T11', 'T2', 'T8'],
content: 'thisIsContent2'
});
// // Checking received meta
expect(test.body.meta).toBeDefined();
expect(test.body.meta.total).toBeDefined();
expect(test.body.meta.total).toBe(2);
expect(test.body.meta.pagenum).toBeDefined();
expect(test.body.meta.pagenum).toBe(1);
expect(test.body.meta.order).toBeDefined();
expect(test.body.meta.order).toBe(Order.DESC);
expect(test.body.meta.pagesize).toBeDefined();
expect(test.body.meta.pagesize).toBe(10);
expect(test.body.meta.hasNext).toBeDefined();
expect(test.body.meta.hasNext).toBe(false);
expect(test.body.meta.hasPrev).toBeDefined();
expect(test.body.meta.hasPrev).toBe(false);
});
afterAll(async () => {
await app.close();
})
});

9
src/test/jest-e2e.json Normal file
View File

@ -0,0 +1,9 @@
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": "e2e.spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}

View File

@ -1,112 +1,318 @@
// // import { CallHandler, ExecutionContext } from "@nestjs/common";
// import { HttpModule } from "@nestjs/axios";
// import { Test } from "@nestjs/testing";
// import { Observable, of } from "rxjs";
// import { PapersController } from "src/application";
// import { Order } from "src/core/domain";
// import { PageDto, SearchQueryDto } from "src/core/domain/dtos";
// import { PageInterceptor } from "src/core/interceptors/page.interceptor";
// import { SearchService } from "src/core/services/common/search.service";
import { HttpService } from "@nestjs/axios";
import { ConfigModule } from "@nestjs/config";
import { Test } from "@nestjs/testing";
import { Observable, of } from "rxjs";
import { EsTime, Order } from "src/core/domain";
import { PageDto } from "src/core/domain/dtos";
import { HttpResponseException } from "src/core/exceptions";
import { PageInterceptor } from "src/core/interceptors/page.interceptor";
// const executionContext = {
// switchToHttp: jest.fn().mockReturnThis(),
// getRequest: jest.fn().mockReturnThis(),
// getHandler: jest.fn().mockReturnThis(),
// getArgs: jest.fn().mockReturnThis(),
// getArgByIndex: jest.fn().mockReturnThis(),
// switchToRpc: jest.fn().mockReturnThis(),
// switchToWs: jest.fn().mockReturnThis(),
// getType: jest.fn().mockReturnThis(),
// getClass: jest.fn().mockReturnThis(),
// };
const execCtxMock = {
switchToHttp: jest.fn().mockReturnThis(),
getRequest: jest.fn().mockReturnThis(),
getHandler: jest.fn().mockReturnThis(),
getArgs: jest.fn().mockReturnThis(),
getArgByIndex: jest.fn().mockReturnThis(),
switchToRpc: jest.fn().mockReturnThis(),
switchToWs: jest.fn().mockReturnThis(),
getType: jest.fn().mockReturnThis(),
getClass: jest.fn().mockReturnThis(),
};
// const callHandler = {
// handle: jest.fn(),
// };
const callHandlerMock = {
handle: jest.fn(),
};
// describe('Testing PageInterceptor', () => {
// let pageInter: PageInterceptor;
// let moduleRef;
describe('Unit tests for PageInterceptor', () => {
let pageInter: PageInterceptor;
let httpService: HttpService;
// beforeEach(async () => {
// moduleRef = await Test.createTestingModule({
// imports: [HttpModule],
// controllers: [PapersController],
// providers: [SearchService, PageInterceptor],
// }).compile();
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
{
provide: HttpService,
useValue: {
post: jest.fn(),
delete: jest.fn()
},
},
PageInterceptor,
],
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
expandVariables: true,
})
],
}).compile();
// pageInter = moduleRef.get(PageInterceptor);
// });
pageInter = moduleRef.get(PageInterceptor);
httpService = moduleRef.get(HttpService);
// describe('intercept()', () => {
// it('Should be defined', () => {
// expect(pageInter).toBeDefined();
// });
execCtxMock.getRequest.mockReturnValue({
query: {
query: 'thisIsMyQuery',
page: 1,
limit: 5,
order: Order.DESC
}
});
// it('Should return an Observable with a page of type PageDto', (done) => {
// executionContext.getRequest.mockReturnValue( { query: new SearchQueryDto('someQuery', 1, 10, 'desc') });
// callHandler.handle.mockReturnValue( of({
// total: { value: 1 },
// hits: [{},],
// }));
callHandlerMock.handle.mockReturnValue(
of({
hits: {
total: { value: 1 },
hits: [{}]
}
})
);
});
it('Should be defined', () => {
expect(pageInter).toBeDefined();
expect(httpService).toBeDefined();
});
describe('intercept()', () => {
let tmp;
beforeAll(() => {
tmp = pageInter.getPIT;
pageInter.getPIT = jest.fn().mockReturnValue({});
});
afterAll(() => {
pageInter.getPIT = tmp;
});
it('Should return a Promise', () => {
expect(pageInter.intercept(execCtxMock, callHandlerMock)).toBeInstanceOf(Promise);
});
it('Should return a Promise with Observable and PageDto inside', () => {
pageInter.intercept(execCtxMock, callHandlerMock).then((res) => {
expect(res).toBeInstanceOf(Observable);
res.subscribe((data) => {
expect(data).toBeInstanceOf(PageDto);
});
});
});
it('Should touch CallHandler.handle() method', () => {
let chHandleSpy = jest.spyOn(callHandlerMock, 'handle');
pageInter.intercept(execCtxMock, callHandlerMock);
expect(chHandleSpy).toBeCalled();
});
it('Should construct a page with proper data on it', () => {
callHandlerMock.handle.mockReturnValueOnce(
of({
hits: {
total: { value: 1 },
hits: [{
_source: {
dummy: 'dum'
}
}]
}
})
);
pageInter.intercept(execCtxMock, callHandlerMock).then((res) => {
res.subscribe((page) => {
expect(page.data.length).toBe(1);
expect(page.data[0]).toEqual({ dummy: 'dum' });
});
});
});
it('Should construct correct meta-data of the page', () => {
execCtxMock.getRequest.mockReturnValueOnce({
query: {
page: 5,
order: 'desc',
limit: 100,
}
});
callHandlerMock.handle.mockReturnValueOnce(
of({
hits: {
total: { value: 921 },
hits: []
}
})
);
pageInter.intercept(execCtxMock, callHandlerMock).then((res) => {
res.subscribe((page) => {
expect(page.meta).toEqual({
total: 921,
pagenum: 5,
order: 'desc',
hasNext: true,
hasPrev: true,
pagesize: 100
});
});
});
});
it('Should reverse the search results', () => {
execCtxMock.getRequest.mockReturnValueOnce({
query: {
page: 1,
order: 'desc',
limit: 3
}
});
pageInter['prevSearch']._prevPage = 3;
pageInter['prevSearch'].isSet = jest.fn().mockImplementationOnce(() => {
return true;
})
callHandlerMock.handle.mockReturnValueOnce(
of({
hits: {
total: { value: 1 },
hits: [
{ sort: ['1', 'less relevant'], _source: '1' },
{ sort: ['2', 'average'], _source: '2' },
{ sort: ['3', 'most relevant'], _source: '3' }
]
}
})
);
pageInter.intercept(execCtxMock, callHandlerMock).then((res) => {
res.subscribe((page) => {
expect(pageInter['prevSearch']._tiebreaker).toEqual(['1', 'less relevant']);
expect(page.data).toEqual(['3', '2', '1']);
});
});
});
});
describe('getPIT()', () => {
it('Should touch HttpService.post() method', () => {
let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({
data: {id: '2567'},
status: 0,
statusText: '',
headers: {},
config: {},
}));
pageInter.getPIT(1);
expect(httpPostMock).toHaveBeenCalled();
});
it('Should contain correct port in the URI from .env', () => {
let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({
data: {id: '2567'},
status: 0,
statusText: '',
headers: {},
config: {},
}));
pageInter.getPIT(1);
expect(httpPostMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/papers/_pit?keep_alive=1m`);
});
it('Should touch HttpService with correct URI when time alive and time-unit are set', () => {
let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({
data: {id: '2567'},
status: 0,
statusText: '',
headers: {},
config: {},
}));
let time = 2;
let unit = EsTime.sec;
// expect(pageInter.intercept(executionContext, callHandler)).toBeInstanceOf(Observable);
// pageInter.intercept(executionContext, callHandler).subscribe((data) => {
// expect(data).toBeInstanceOf(PageDto);
// done();
// });
// })
pageInter.getPIT(time, unit);
expect(httpPostMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/papers/_pit?keep_alive=${time+unit}`);
});
// it('Should hold content on the returned page', (done) => {
// executionContext.getRequest.mockReturnValueOnce( { query: new SearchQueryDto('someQuery', 1, 10, 'desc') });
// callHandler.handle.mockReturnValueOnce(of({
// total: { value: 1 },
// hits: [{dummy: 'dum'}],
// }));
it('Should return error exeception when HttpService fails', () => {
jest.spyOn(httpService, 'post').mockImplementationOnce(() => {
throw HttpResponseException;
});
// pageInter.intercept(executionContext, callHandler).subscribe((data) => {
// expect(data).toEqual({
// data: expect.anything(),
// meta: expect.anything(),
// });
// done();
// });
// });
expect(pageInter.getPIT(1)).rejects.toEqual(HttpResponseException);
});
// it('Should have next page', (done) => {
// executionContext.getRequest.mockReturnValue({ query: new SearchQueryDto('someQuery', 1, 5, 'desc') });
// callHandler.handle.mockReturnValue(of({
// total: { value: 10 },
// hits: Array(10).fill({dummy: 'dum'}, 0, 10),
// }));
it('Should return a non-empty string when HttpService request succeedes', () => {
jest.spyOn(httpService, 'post').mockReturnValueOnce(of({
data: {id: '2567', keep_alive: '1m'},
status: 0,
statusText: '',
headers: {},
config: {},
}));
// pageInter.intercept(executionContext, callHandler).subscribe((data) => {
// expect(data.meta.hasNext).toEqual(true);
// expect(data.meta.hasPrev).toEqual(false);
// done();
// });
// });
expect(pageInter.getPIT(1)).resolves.toEqual({
id: '2567',
keep_alive: '1m',
});
});
});
// it('Should have correct meta-data', (done) => {
// executionContext.getRequest.mockReturnValue({ query: new SearchQueryDto('someQuery', 2, 5, 'asc') });
// callHandler.handle.mockReturnValue(of({
// total: { value: 15 },
// hits: Array(15).fill({dummy: 'dum'}, 0, 15),
// }));
describe('deletePIT()', () => {
it('Should touch HttpService.delete() method', () => {
let httpDeleteMock = jest.spyOn(httpService, 'delete').mockReturnValueOnce(
of({
data: {succeeded: true},
status: 0,
statusText: '',
headers: {},
config: {},
}));
// pageInter.intercept(executionContext, callHandler).subscribe((data) => {
// expect(data.meta).toEqual({
// total: 15,
// pagenum: 2,
// order: Order.ASC,
// hasNext: true,
// hasPrev: true,
// pagesize: 5
// });
// done();
// });
// });
// });
pageInter.deletePIT('');
expect(httpDeleteMock).toHaveBeenCalled();
});
// });
it('Should contain correct port in the URI from .env and passed PIT ID in the request body', () => {
let httpDeleteMock = jest.spyOn(httpService, 'delete').mockReturnValueOnce(
of({
data: { succeeded: true },
status: 0,
statusText: '',
headers: {},
config: {},
}));
pageInter.deletePIT('thisIsIDSpecified');
expect(httpDeleteMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/_pit`, {
data: { id: 'thisIsIDSpecified' },
headers: { 'Content-Type': 'application/json' }
});
});
it('Should return error exeception when HttpService fails', () => {
jest.spyOn(httpService, 'delete').mockImplementationOnce(() => {
throw HttpResponseException;
});
expect(pageInter.deletePIT('')).rejects.toEqual(HttpResponseException);
});
it('Should return true when Elasticsearch successfully removed PIT', () => {
jest.spyOn(httpService, 'delete').mockReturnValueOnce(
of({
data: { succeeded: true },
status: 0,
statusText: '',
headers: {},
config: {},
}));
expect(pageInter.deletePIT('')).resolves.toBe(true);
});
});
});

View File

@ -0,0 +1,140 @@
import { HttpModule } from "@nestjs/axios";
import { NotFoundException } from "@nestjs/common";
import { Test } from "@nestjs/testing";
import { PapersController } from "src/application";
import { SearchService } from "src/core/services/common/search.service";
describe('Unit tests for PapersController', () => {
let searchService: SearchService;
let papersController: PapersController;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
PapersController,
{
provide: SearchService,
useValue: {
findByContext: jest.fn(),
findByID: jest.fn()
}
}
],
imports: [HttpModule]
}).compile();
papersController = moduleRef.get(PapersController);
searchService = moduleRef.get(SearchService);
});
it('Should be defined', () => {
expect(papersController).toBeDefined();
expect(searchService).toBeDefined();
});
describe('getByContext()', () => {
it('Should touch SearchService.findByContext() method', () => {
let findCtxMock = jest.spyOn(searchService, 'findByContext')
.mockResolvedValueOnce({
took: undefined,
timed_out: undefined,
hits: undefined,
_shards: undefined,
});
papersController.getByContext({ query: undefined });
expect(findCtxMock).toHaveBeenCalled();
});
it('Should resolve, when searched successfully', () => {
const searchResultMock = {
took: 1,
timed_out: false,
hits: {
total: {},
hits: [
{
_source: {
id: 'thisIsID',
title: 'andThisIsTheTitle',
authors: ['alsoAuthors'],
topic: 'andThatIsTheTopic',
summary: 'someSummaries',
tags: ['tag1', 'tag2'],
content: 'finallyContent!'
}
}
],
},
_shards: undefined,
};
jest.spyOn(searchService, 'findByContext')
.mockResolvedValueOnce(searchResultMock);
expect(papersController.getByContext({ query: undefined })).resolves.toEqual(searchResultMock);
});
it('Should throw, when search was unsuccessful', () => {
searchService.findByContext = jest.fn()
.mockRejectedValueOnce(new NotFoundException);
expect(papersController.getByContext({ query: undefined }))
.rejects.toThrow(NotFoundException)
});
});
describe('getByID()', () => {
it('Should touch SearchService.findByID() method', () => {
let findIDMock = jest.spyOn(searchService, 'findByID')
.mockResolvedValueOnce({
took: undefined,
timed_out: undefined,
hits: { total: {}, hits:[{ _source: undefined }] },
_shards: undefined,
});
papersController.getByID('');
expect(findIDMock).toHaveBeenCalled();
});
it('Should resolve the document, when searched successfully', () => {
const searchResultMock = {
took: 1,
timed_out: false,
hits: {
total: {},
hits: [
{
_source: {
id: 'thisIsID',
title: 'andThisIsTheTitle',
authors: ['alsoAuthors'],
topic: 'andThatIsTheTopic',
summary: 'someSummaries',
tags: ['tag1', 'tag2'],
content: 'finallyContent!'
}
}
],
},
_shards: undefined,
};
jest.spyOn(searchService, 'findByID')
.mockResolvedValueOnce(searchResultMock);
expect(papersController.getByID(''))
.resolves.toEqual(searchResultMock.hits.hits[0]._source);
});
it('Should throw, when search was unsuccessful', () => {
searchService.findByID = jest.fn()
.mockRejectedValueOnce(new NotFoundException);
expect(papersController.getByID(''))
.rejects.toThrow(NotFoundException)
});
});
});

View File

@ -1,50 +0,0 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import { SearchModule } from "src/infrastructure/modules/search.module";
import request from 'supertest'
import { assert } from "console";
import { resolve } from "path";
import { AppModule } from "src/infrastructure/modules";
describe('E2E Testing of /papers', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('Should return one, exact item on page', async () => {
return request(app.getHttpServer())
.get('/papers/eeeb2d01-8315-454e-b33f-3d6caa25db42')
.expect(200)
.expect((res) => {
res.body.data.length === 1;
})
.expect((res) => {
res.body.data[0]._source.id === 'eeeb2d01-8315-454e-b33f-3d6caa25db42';
});
});
it('Should return multiple items', async () => {
return request(app.getHttpServer())
.get('/papers/search?query=at&page=1')
.expect(200)
.expect((res) => {
res.body.data.length > 0;
})
.expect((res) => {
for (const value of res.body.data) {
if(Object.keys(value).length === 0) return false;
}
})
});
afterAll(async () => {
await app.close();
})
});

View File

@ -1,112 +1,224 @@
// import { HttpService } from "@nestjs/axios";
// import { ConfigModule } from "@nestjs/config";
// import { Test } from "@nestjs/testing";
// import exp from "constants";
// import { of } from "rxjs";
// import { EsTime } from "src/core/domain/enums/es-time.enum";
// import { HttpResponseException } from "src/core/exceptions";
// import { SearchService } from "src/core/services/common/search.service";
import { HttpService } from "@nestjs/axios";
import { GatewayTimeoutException, HttpException } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { Test } from "@nestjs/testing";
import { of } from "rxjs";
import { EsQueryDto, EsResponseDto } from "src/core/domain";
import { SearchService } from "src/core/services/common/search.service";
// describe('Unit tests for SearchService', () => {
// let searchService: SearchService;
// let httpService: HttpService;
describe('Unit tests for SearchService', () => {
let searchService: SearchService;
let httpService: HttpService;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
SearchService,
{
provide: HttpService,
useValue: {
get: jest.fn(),
},
},
],
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
expandVariables: true,
})
],
}).compile();
// beforeAll(async () => {
// const moduleRef = await Test.createTestingModule({
// providers: [
// SearchService,
// {
// provide: HttpService,
// useValue: {
// post: jest.fn(),
// },
// },
// ],
// imports: [
// ConfigModule.forRoot({
// isGlobal: true,
// cache: true,
// expandVariables: true,
// })
// ],
// }).compile();
searchService = moduleRef.get(SearchService);
httpService = moduleRef.get(HttpService);
});
// searchService = moduleRef.get(SearchService);
// httpService = moduleRef.get(HttpService);
// });
describe('findByID()', () => {
it('Should touch HttpService.get() method', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
// describe('getPIT()', () => {
// it('Should touch HttpService.post() method', () => {
// let postMock = jest.spyOn(httpService, 'post').mockReturnValue(of({
// data: {id: '2567'},
// status: 0,
// statusText: '',
// headers: {},
// config: {},
// }));
searchService.findByID('');
expect(httpGetSpy).toHaveBeenCalled();
});
// searchService.getPIT(1);
// expect(postMock).toHaveBeenCalled();
// });
// it('Should contain correct port in the URI from .env', () => {
// let postMock = jest.spyOn(httpService, 'post').mockReturnValue(of({
// data: {id: '2567'},
// status: 0,
// statusText: '',
// headers: {},
// config: {},
// }));
// searchService.getPIT(1);
// expect(postMock).toHaveBeenCalledWith(`http://localhost:${process.env.ES_PORT}/papers/_pit?keep_alive=1m`);
// });
// it('Should touch HttpService with correct URI when time alive and time-unit are set', () => {
// let postMock = jest.spyOn(httpService, 'post').mockReturnValue(of({
// data: {id: '2567'},
// status: 0,
// statusText: '',
// headers: {},
// config: {},
// }));
// let time = 2;
// let unit = EsTime.sec;
it('Should send correct data via HttpService.get() body parameter', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
// searchService.getPIT(time, unit);
// expect(postMock).toHaveBeenCalledWith(`http://localhost:${process.env.ES_PORT}/papers/_pit?keep_alive=${time+unit}`);
// });
const uuid = 'thisIsUUID_Provided';
searchService.findByID(uuid);
expect(httpGetSpy).toHaveBeenCalledWith<[string, object]>(expect.anything(), {
data: {
size: 1,
query: {
query_string: {
query: 'id:' + uuid
}
}
},
headers: { 'Content-Type': 'application/json' }
});
});
// it('Should return error exeception when HttpService fails', () => {
// jest.spyOn(httpService, 'post').mockImplementation(() => {
// throw HttpResponseException;
// });
it('Should call HttpService.get() with correct URI and port number', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
searchService.findByID('');
expect(httpGetSpy).toHaveBeenCalledWith<[string, object]>(
`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/_search`,
expect.anything()
);
});
// expect(searchService.getPIT(1)).rejects.toEqual(HttpResponseException);
// });
it('Should return a Promise', () => {
expect(searchService.findByID('')).toBeInstanceOf(Promise);
});
// it('Should return a non-empty string when HttpService request succeedes', () => {
// jest.spyOn(httpService, 'post').mockReturnValue(of({
// data: {id: '2567', keep_alive: '1m'},
// status: 0,
// statusText: '',
// headers: {},
// config: {},
// }));
// it('Should return a Promise with EsResponseDto', () => {
// // Axios response mock
// httpService.get = jest.fn().mockReturnValueOnce(
// of({
// status: undefined,
// statusText: undefined,
// headers: undefined,
// config: undefined,
// data: {
// took: 1,
// timed_out: false,
// hits: {
// total: {},
// hits: [{}]
// }
// },
// })
// );
// expect(searchService.findByID('')).resolves.toBeInstanceOf(EsResponseDto)
// });
// expect(searchService.getPIT(1)).resolves.toEqual({
// id: '2567',
// keep_alive: '1m',
// });
// });
// Errors
it('Should throw 504 | GatewayTimeoutException', () => {
// Axios response mock
httpService.get = jest.fn().mockReturnValueOnce(
of({
status: undefined,
statusText: undefined,
headers: undefined,
config: undefined,
data: {
timed_out: true,
dummy: 'dum'
}
})
);
searchService.findByID('').catch((err) => {
expect(err).toBeInstanceOf(GatewayTimeoutException);
});
});
// });
it('Should throw an HttpException when HttpService.get() fails and throws', () => {
httpService.get = jest.fn().mockImplementationOnce(() => {
throw new HttpException({ oops: 'sorry' }, 999);
});
// describe('deletePIT()', () => {
// it.todo('Should fail to delete, because the requested PIT ID is invalid');
// it.todo('Should call HttpService.delete() method with correct body');
// });
// });
searchService.findByID('').catch((err) => {
expect(err).toBeInstanceOf(HttpException);
expect(err.response).toEqual({ oops: 'sorry' });
expect(err.status).toEqual(999);
});
});
});
describe('findByContext()', () => {
it('Should touch HttpService.get() method', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
searchService.findByContext(null);
expect(httpGetSpy).toHaveBeenCalled();
});
it('Should send correct data via HttpService.get() body parameter', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
let es_query = new EsQueryDto();
es_query = {
query: {
query_string: {
query: 'thisIsTheQuery!'
}
}
}
searchService.findByContext(es_query);
expect(httpGetSpy).toHaveBeenCalledWith<[string, object]>(expect.anything(), {
data: es_query,
headers: { 'Content-Type': 'application/json' }
});
});
it('Should call HttpService.get() with correct URI and port number', () => {
let httpGetSpy = jest.spyOn(httpService, 'get');
searchService.findByContext(null);
expect(httpGetSpy).toHaveBeenCalledWith<[string, object]>(
`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/_search`,
expect.anything()
);
});
it('Should return a Promise', () => {
expect(searchService.findByContext(null)).toBeInstanceOf(Promise);
});
// it('Should return a Promise with EsResponseDto', () => {
// // Axios response mock
// httpService.get = jest.fn().mockReturnValueOnce(
// of({
// status: undefined,
// statusText: undefined,
// headers: undefined,
// config: undefined,
// data: {
// dummy: 'dum'
// }
// })
// );
// expect(searchService.findByContext(null)).resolves.toMatchObject<EsResponseDto>(null);
// });
// Errors
it('Should throw 504 | GatewayTimeoutException', () => {
// Axios response mock
httpService.get = jest.fn().mockReturnValueOnce(
of({
status: undefined,
statusText: undefined,
headers: undefined,
config: undefined,
data: {
timed_out: true,
dummy: 'dum'
}
})
);
searchService.findByContext(null).catch((err) => {
expect(err).toBeInstanceOf(GatewayTimeoutException);
});
});
it('Should throw an HttpException when HttpService.get() fails and throws', () => {
httpService.get = jest.fn().mockImplementationOnce(() => {
throw new HttpException({ oops: 'sorry' }, 999);
});
searchService.findByContext(null).catch((err) => {
expect(err).toBeInstanceOf(HttpException);
expect(err.response).toEqual({ oops: 'sorry' });
expect(err.status).toEqual(999);
});
});
});
});