Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • web-et-numerique/factory/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server
1 result
Select Git revision
Show changes
Showing
with 503 additions and 229 deletions
import { HttpModule } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigurationModule } from '../configuration/configuration.module';
import { TagEnum } from './enums/tag.enum';
......@@ -309,15 +309,15 @@ describe('PostsService', () => {
};
it('should format post with no custom expert', () => {
const objCp = { ...postToFormat };
let result = { ...postToFormat };
const result = { ...postToFormat };
result.excerpt = 'Inconnu';
result.feature_image = 'https://localhost/blog/content/images/2021/12/dacc-4.png';
expect(service.formatPosts(objCp)).toEqual(result);
});
it('should format post with custom expert', () => {
let objCp = { ...postToFormat };
const objCp = { ...postToFormat };
objCp.custom_excerpt = 'Toto';
let result = { ...postToFormat };
const result = { ...postToFormat };
result.custom_excerpt = 'Toto';
result.feature_image = 'https://localhost/blog/content/images/2021/12/dacc-4.png';
expect(service.formatPosts(objCp)).toEqual(result);
......
import { SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { Day } from './day.schema';
import { Day } from '../../structures/schemas/day.schema';
export type WeekDocument = Week & Document;
......
import { ConfigurationService } from '../configuration/configuration.service';
import { Page } from '../pages/schemas/page.schema';
import { Post } from '../posts/schemas/post.schema';
import { UserRole } from '../users/enum/user-role.enum';
import { User } from '../users/schemas/user.schema';
export function rewriteGhostImgUrl(configService: ConfigurationService, itemData: Page | Post): Page | Post {
// Handle image display. Rewrite image URL to fit ghost infra issue.
......@@ -15,3 +17,37 @@ export function rewriteGhostImgUrl(configService: ConfigurationService, itemData
}
return itemData;
}
export function hasAdminRole(user: User): boolean {
return user.role === UserRole.admin;
}
export const es_settings_homemade_french = {
analysis: {
filter: {
french_elision: {
type: 'elision',
articles_case: true,
articles: ['l', 'm', 't', 'qu', 'n', 's', 'j', 'd', 'c', 'jusqu', 'quoiqu', 'lorsqu', 'puisqu'],
},
french_stop: {
type: 'stop',
stopwords: '_french_',
},
french_stemmer: {
type: 'stemmer',
language: 'minimal_french',
},
},
analyzer: {
homemade_french: {
tokenizer: 'standard',
filter: ['lowercase', 'asciifolding', 'french_elision', 'french_stop', 'french_stemmer'],
},
homemade_french_stopless: {
tokenizer: 'standard',
filter: ['lowercase', 'asciifolding', 'french_elision', 'french_stemmer'],
},
},
},
};
export type PhotonResponse = {
features: [{ geometry: { coordinates: [number, number] }; properties: { [key: string]: string } }];
type: string;
};
import { Type } from 'class-transformer';
import { IsNotEmpty, ValidateNested } from 'class-validator';
import { structureDto } from './structure.dto';
import { StructureDto } from './structure.dto';
export class CreateStructureDto {
@ValidateNested({ each: true })
@Type(() => structureDto)
structure: structureDto;
@Type(() => StructureDto)
structure: StructureDto;
@IsNotEmpty()
idUser: string;
......
import { Type } from 'class-transformer';
import { ArrayNotEmpty, IsNotEmpty, ValidateNested } from 'class-validator';
import { ArrayNotEmpty, IsArray, IsNotEmpty, ValidateNested } from 'class-validator';
import { Address } from '../schemas/address.schema';
import { Week } from '../schemas/week.schema';
import { Week } from '../../shared/schemas/week.schema';
import { PersonalOfferDocument } from '../../personal-offers/schemas/personal-offer.schema';
import { ApiProperty } from '@nestjs/swagger';
export class structureDto {
export class StructureDto {
numero: string;
createdAt: Date;
updatedAt: Date;
......@@ -12,6 +14,7 @@ export class structureDto {
@IsNotEmpty()
structureName: string;
@ApiProperty({ required: true })
@IsNotEmpty()
structureType: string;
......@@ -21,9 +24,9 @@ export class structureDto {
@Type(() => Address)
address: Address;
@IsNotEmpty()
contactPhone: string;
@ApiProperty({ required: true })
@IsNotEmpty()
contactMail: string;
......@@ -35,8 +38,11 @@ export class structureDto {
lockdownActivity: string;
otherDescription: string;
@ApiProperty({ required: true })
@IsNotEmpty()
pmrAccess: boolean;
publicsAccompaniment: string[];
proceduresAccompaniment: string[];
remoteAccompaniment: boolean;
......@@ -47,18 +53,31 @@ export class structureDto {
@ArrayNotEmpty()
publics: string[];
@ApiProperty({ required: true })
@IsNotEmpty()
freeWorkShop: boolean;
freeWorkShop: boolean | string;
@ApiProperty({ required: true })
@IsNotEmpty()
nbComputers: number;
@ApiProperty({ required: true })
@IsNotEmpty()
nbPrinters: number;
@ApiProperty({ required: true })
@IsNotEmpty()
nbTablets: number;
@ApiProperty({ required: true })
@IsNotEmpty()
nbNumericTerminal: number;
@ApiProperty({ required: true })
@IsNotEmpty()
nbScanners: number;
exceptionalClosures: string;
equipmentsAndServices: string[];
hours: Week;
......@@ -68,6 +87,9 @@ export class structureDto {
socialAndProfessional: string[];
digitalCultureSecurity: string[];
coord: number[];
accountVerified: boolean;
accountVerified = true; // Value is forced to true because there is no verification needed now. Cannot be removed because of old data prod
dataShareConsentDate: Date;
@IsArray()
personalOffers: PersonalOfferDocument[];
}
import { PartialType } from '@nestjs/mapped-types';
import { StructureDto } from './structure.dto';
export class UpdateStructureDto extends PartialType(StructureDto) {}
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Type } from 'class-transformer';
import { ArrayNotEmpty, IsNotEmpty, ValidateNested } from 'class-validator';
import { Document } from 'mongoose';
import { Document, Types } from 'mongoose';
import { PersonalOfferDocument } from '../../personal-offers/schemas/personal-offer.schema';
import { Week } from '../../shared/schemas/week.schema';
import { Address } from './address.schema';
import { Week } from './week.schema';
export type StructureDocument = Structure & Document;
......@@ -50,6 +51,7 @@ export class Structure {
this.deletedAt = data.deletedAt;
this.accountVerified = data.accountVerified;
this.dataShareConsentDate = data.dataShareConsentDate;
this.personalOffers = data.personalOffers;
}
@Prop()
......@@ -148,7 +150,7 @@ export class Structure {
@Prop()
@IsNotEmpty()
freeWorkShop: boolean;
freeWorkShop: boolean | string;
@Prop()
nbComputers: number;
......@@ -184,6 +186,9 @@ export class Structure {
@Prop()
dataShareConsentDate: Date;
@Prop({ type: [{ type: Types.ObjectId, ref: 'PersonalOffer' }] })
personalOffers: PersonalOfferDocument[];
@Prop()
createdAt: Date;
......
import { HttpService, Injectable } from '@nestjs/common';
import { Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { AxiosResponse } from 'axios';
import { HttpService } from '@nestjs/axios';
import { Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Cron, CronExpression } from '@nestjs/schedule';
import * as _ from 'lodash';
import { AxiosResponse } from 'axios';
import * as https from 'https';
import { InjectModel } from '@nestjs/mongoose';
import * as _ from 'lodash';
import { Model } from 'mongoose';
import { Structure, StructureDocument } from '../schemas/structure.schema';
import { ApticStructure } from '../schemas/aptic-structure.schema';
import { Observable } from 'rxjs';
import { CategoriesFormationsService } from '../../categories/services/categories-formations.service';
import { UsersService } from '../../users/services/users.service';
import { Address } from '../schemas/address.schema';
import { UsersService } from '../../users/users.service';
import { ApticCatalog } from '../schemas/aptic-catalog.schema';
import { CategoriesFormationsService } from '../../categories/services/categories-formations.service';
import { ApticStructure } from '../schemas/aptic-structure.schema';
import { Structure, StructureDocument } from '../schemas/structure.schema';
import { StructuresSearchService } from './structures-search.service';
@Injectable()
......
import { HttpModule } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { HttpStatus } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import * as bcrypt from 'bcrypt';
import { Types } from 'mongoose';
import { personalOffersDataMock } from '../../../test/mock/data/personalOffers.mock.data';
import { structureMockDto, structuresDocumentDataMock } from '../../../test/mock/data/structures.mock.data';
import { mockParametersModel } from '../../../test/mock/services/parameters.mock.service';
import { UsersServiceMock } from '../../../test/mock/services/user.mock.service';
import { CategoriesFormationsService } from '../../categories/services/categories-formations.service';
......@@ -11,10 +14,11 @@ import { ConfigurationService } from '../../configuration/configuration.service'
import { MailerModule } from '../../mailer/mailer.module';
import { ParametersService } from '../../parameters/parameters.service';
import { Parameters } from '../../parameters/schemas/parameters.schema';
import { PersonalOfferDocument } from '../../personal-offers/schemas/personal-offer.schema';
import { SearchModule } from '../../search/search.module';
import { IUser } from '../../users/interfaces/user.interface';
import { UsersService } from '../../users/users.service';
import { structureDto } from '../dto/structure.dto';
import { UsersService } from '../../users/services/users.service';
import { StructureDto } from '../dto/structure.dto';
import { Structure, StructureDocument } from '../schemas/structure.schema';
import { StructuresSearchService } from './structures-search.service';
import { StructuresService } from './structures.service';
......@@ -32,6 +36,8 @@ describe('StructuresService', () => {
findOne: jest.fn(),
exec: jest.fn(),
find: jest.fn(),
populate: jest.fn(),
save: jest.fn(),
};
const structuresSearchServiceMock = {
......@@ -79,7 +85,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
{
closing: '17:00',
......@@ -92,7 +98,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
{
closing: '17:00',
......@@ -105,7 +111,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
{
closing: '17:00',
......@@ -118,7 +124,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
{
closing: '17:00',
......@@ -131,7 +137,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
],
},
......@@ -155,8 +161,12 @@ describe('StructuresService', () => {
linkedin: null,
nbScanners: 1,
otherDescription: null,
personalOffers: [],
},
]),
dropIndex: jest.fn(),
createStructureIndex: jest.fn(),
indexStructure: jest.fn(),
};
const mockCategoriesFormationsService = {
......@@ -258,14 +268,20 @@ describe('StructuresService', () => {
expect(service).toBeDefined();
});
it('should Initiate structure', () => {
const res = service.initiateStructureIndex();
expect(res).toBeTruthy();
it('should Initiate structure index', async () => {
const spyerIndex = jest.spyOn(structuresSearchServiceMock, 'indexStructure');
const spyerDrop = jest.spyOn(structuresSearchServiceMock, 'dropIndex');
const spyerCreate = jest.spyOn(structuresSearchServiceMock, 'createStructureIndex');
await service.initiateStructureIndex();
expect(spyerCreate).toBeCalledTimes(1);
expect(spyerDrop).toBeCalledTimes(1);
expect(spyerIndex).toBeCalledTimes(1);
});
describe('should searchForStructures', () => {
jest.setTimeout(30000);
mockStructureModel.find.mockReturnThis();
mockStructureModel.populate.mockReturnThis();
mockStructureModel.exec.mockResolvedValue([
{
_id: '6903ba0e2ab5775cfc01ed4d',
......@@ -310,7 +326,7 @@ describe('StructuresService', () => {
time: [
{
closing: '12:30',
opening: '9:00',
opening: '09:00',
},
{
closing: '17:00',
......@@ -330,6 +346,7 @@ describe('StructuresService', () => {
linkedin: null,
nbScanners: 1,
otherDescription: null,
personalOffers: [],
},
]);
......@@ -351,12 +368,25 @@ describe('StructuresService', () => {
});
});
it('should create structure', () => {
const structure = new structureDto();
let res = service.create(null, structure);
expect(res).toBeTruthy();
res = service.create('tsfsf6296', structure);
expect(res).toBeTruthy();
describe('create', () => {
it('should return invalid profile', async () => {
const structure = structureMockDto;
try {
await service.create('test@test.com', structure);
expect(true).toBe(false);
} catch (e) {
expect(e.message).toEqual('Invalid profile');
expect(e.status).toEqual(HttpStatus.NOT_FOUND);
}
});
// it('should have valid profile and create structure', async () => {
// const structure = structureMockDto;
// const spyer = jest.spyOn(mockStructureModel, 'save');
// const spyerIndex = jest.spyOn(structuresSearchServiceMock, 'indexStructure');
// await service.create('pauline.dupont@mii.com', structure);
// expect(spyer).toBeCalledTimes(1); // Should create structure in DB
// expect(spyerIndex).toBeCalledTimes(1); // Should index structure
// });
});
it('should search structure', () => {
......@@ -409,9 +439,11 @@ describe('StructuresService', () => {
],
pendingStructuresLink: [],
structureOutdatedMailSent: [],
personalOffers: [],
name: 'Jacques',
surname: 'Dupont',
phone: '06 06 06 06 06',
createdAt: new Date('2022-05-25T09:48:28.824Z'),
} as IUser;
jest
.spyOn(service, 'findOne')
......@@ -458,9 +490,11 @@ describe('StructuresService', () => {
],
pendingStructuresLink: [],
structureOutdatedMailSent: [],
personalOffers: [],
name: 'Jacques',
surname: 'Dupont',
phone: '06 06 06 06 06',
createdAt: new Date('2022-05-25T09:48:28.824Z'),
} as IUser;
jest
.spyOn(service, 'findOne')
......@@ -497,4 +531,37 @@ describe('StructuresService', () => {
res = service.reportStructureError(null, '');
expect(res).toBeTruthy();
});
describe('addPersonalOffer', () => {
const personalOfferDocumentMock: PersonalOfferDocument = personalOffersDataMock[0] as PersonalOfferDocument;
it('should add personal offer to the structure', async () => {
jest.spyOn(service, 'findOne').mockResolvedValue(structuresDocumentDataMock[0]);
const expectedResult = { ...structuresDocumentDataMock[0], personalOffers: [personalOfferDocumentMock] };
expect(await service.addPersonalOffer('6093ba0e2ab5775cfc01ed3e', personalOfferDocumentMock)).toEqual(
expectedResult
);
});
it('should return exception if structure is not found for given id', async () => {
jest.spyOn(service, 'findOne').mockResolvedValue(null);
try {
await service.addPersonalOffer('abcd', personalOfferDocumentMock);
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe('Structure not found for the personal offer attachment');
expect(e.status).toBe(400);
}
});
it('should return exception if personal offer already exists in the structure', async () => {
jest.spyOn(service, 'findOne').mockResolvedValue(structuresDocumentDataMock[1]);
try {
await service.addPersonalOffer('6093ba0e2ab5775cfc01ed3e', personalOfferDocumentMock);
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe('Personal offer already exist in the structure');
expect(e.status).toBe(400);
}
});
});
});
......@@ -23,35 +23,44 @@ describe('StructuresSearchService', () => {
service = module.get<StructuresSearchService>(StructuresSearchService);
service['index'] = 'structures-unit-test';
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should create index', async () => {
await service.dropIndex();
const res = await service.createStructureIndex();
expect(res).toBeTruthy();
});
await service.createStructureIndex();
it('should index structures', async () => {
// init str
const structuresForSearchService = new StructuresForSearchServiceMock();
const structures = structuresForSearchService.findAll();
const res = await Promise.all(
await Promise.all(
structures.map((structure: any) => {
service.indexStructure(structure);
})
);
expect(res).toBeTruthy();
// wait for the new structures to be indexed before search
await service.refreshIndexStructure();
// but we still need to wait the refresh to be done
await new Promise((r) => setTimeout(r, 2000));
}, 10000);
it('should be defined', () => {
expect(service).toBeDefined();
});
// it('should index structures', async () => {
// const structuresForSearchService = new StructuresForSearchServiceMock();
// const structures = structuresForSearchService.findAll();
// const res = await Promise.all(
// structures.map((structure: any) => {
// service.indexStructure(structure);
// })
// );
// expect(res).toBeTruthy();
// // wait for the new structures to be indexed before search
// await service.refreshIndexStructure();
// // but we still need to wait the refresh to be done
// await new Promise((r) => setTimeout(r, 2000));
// });
it('should find maisons de la métropole', async () => {
const res = await service.search('maison de la');
//Logger.log(JSON.stringify(res));
......@@ -64,12 +73,53 @@ describe('StructuresSearchService', () => {
expect(res[0].structureName).toContain('Métropole');
});
it('should find structure with stopword "son"', async () => {
const res = await service.search('son');
expect(res[0].structureName).toContain('Le Son du Clic');
});
it('should find structure with stopword "mais"', async () => {
const res = await service.search('mais');
expect(res[0].structureName.toLowerCase()).toContain('maison');
});
it('should find structure with stopword "le"', async () => {
const res = await service.search('le');
expect(res.length).toBeGreaterThanOrEqual(1);
});
it('should find structure with partial word "oull"', async () => {
const res = await service.search('oull');
expect(res[0].structureName.toLowerCase()).toContain('oullins');
});
it('should find structure with partial word "vill"', async () => {
const res = await service.search('vill');
expect(res[0].structureName.toLowerCase()).toContain('villeurbanne');
});
it('should find structure with partial word "asso"', async () => {
const res = await service.search('asso');
Logger.log(JSON.stringify(res));
expect(res[0].structureName.toLowerCase()).toContain('association');
});
it('should find text in description', async () => {
const res = await service.search('liseuse');
expect(res.length).toBe(1);
expect(res[0].structureName).toContain("Médiathèque d'Ecully");
});
it('should not find text in description when search by name', async () => {
const res = await service.search('liseuse', ['structureName']);
expect(res.length).toBe(0);
});
it('should find structure when search by name', async () => {
const res = await service.search('le son', ['structureName']);
expect(res[0].structureName).toContain('Le Son du Clic');
});
it('should drop index', async () => {
const res = await service.dropIndex();
expect(res).toBeTruthy();
......
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { ElasticsearchService } from '@nestjs/elasticsearch';
import { structureDto } from '../dto/structure.dto';
import { StructureDocument } from '../schemas/structure.schema';
import { Structure, StructureDocument } from '../schemas/structure.schema';
import { StructureSearchBody } from '../interfaces/structure-search-body.interface';
import { StructureSearchResult } from '../interfaces/structure-search-response.interface';
import { es_settings_homemade_french } from '../../shared/utils';
@Injectable()
export class StructuresSearchService {
private index = 'structures';
private readonly logger = new Logger(StructuresSearchService.name);
constructor(private readonly elasticsearchService: ElasticsearchService) {}
public async indexStructure(structure: StructureDocument): Promise<StructureDocument> {
this.logger.debug(`indexStructure`);
this.elasticsearchService.index<StructureSearchResult, StructureSearchBody>({
index: this.index,
id: structure._id,
......@@ -27,26 +29,40 @@ export class StructuresSearchService {
}
public async createStructureIndex(): Promise<any> {
this.logger.debug(`createStructureIndex`);
// use custom analyzer with minimal_french stemmer to avoid default light_french stemmer problems (oullins -> oulin, "oull" not found)
// don't use stopwords (le, la, de, son, etc.) for structureName (to find "maison de la", "le son du clic", etc.)
return this.elasticsearchService.indices.create({
index: this.index,
body: {
settings: {
analysis: {
analyzer: {
default: {
type: 'french',
},
default_search: {
type: 'french',
mappings: {
dynamic_templates: [
{
strings: {
match_mapping_type: 'string',
mapping: {
type: 'text',
analyzer: 'homemade_french',
search_analyzer: 'homemade_french',
},
},
},
],
properties: {
structureName: {
type: 'text',
analyzer: 'homemade_french_stopless',
search_analyzer: 'homemade_french_stopless',
},
},
},
settings: es_settings_homemade_french,
},
});
}
public async dropIndex(): Promise<any> {
this.logger.debug(`dropIndex`);
if (
(
await this.elasticsearchService.indices.exists({
......@@ -61,6 +77,7 @@ export class StructuresSearchService {
}
public async deleteIndexStructure(structure: StructureDocument): Promise<StructureDocument> {
this.logger.debug(`deleteIndexStructure`);
this.elasticsearchService.delete<StructureSearchResult, StructureSearchBody>({
index: this.index,
id: structure._id,
......@@ -69,12 +86,14 @@ export class StructuresSearchService {
}
public async refreshIndexStructure(): Promise<any> {
this.logger.debug(`refreshIndexStructure`);
return this.elasticsearchService.indices.refresh({
index: this.index,
});
}
public async search(searchString: string): Promise<StructureSearchBody[]> {
public async search(searchString: string, fields?: string[]): Promise<StructureSearchBody[]> {
this.logger.debug(`search ${searchString} | fields : ${fields}`);
searchString = searchString ? searchString + '*' : '*';
const { body } = await this.elasticsearchService.search<StructureSearchResult>({
index: this.index,
......@@ -85,7 +104,7 @@ export class StructuresSearchService {
query_string: {
analyze_wildcard: 'true',
query: searchString,
fields: ['structureName^5', 'structureType^5', 'address.commune^10', 'description'],
fields: fields ? fields : ['structureName^5', 'structureType^5', 'address.commune^10', 'description'],
fuzziness: 'AUTO',
},
},
......@@ -97,7 +116,8 @@ export class StructuresSearchService {
return sortedHits.map((item) => item._source);
}
public async update(structure: structureDto, id: string): Promise<any> {
public async update(structure: Structure, id: string): Promise<any> {
this.logger.debug('update');
return this.elasticsearchService.update({
index: this.index,
id: id,
......
import { Controller, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { StructureType } from './structure-type.schema';
import { StructureTypeService } from './structure-type.service';
@ApiTags('structures')
@Controller('structure-type')
export class StructureTypeController {
constructor(private readonly structureTypeService: StructureTypeService) {}
......
import { HttpService } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
import { CategoriesAccompagnementServiceMock } from '../../test/mock/services/categoriesAccompagnement.mock.service';
import { CategoriesFormationsServiceMock } from '../../test/mock/services/categoriesFormations.mock.service';
......@@ -11,7 +11,7 @@ import { CategoriesAccompagnementService } from '../categories/services/categori
import { CategoriesFormationsService } from '../categories/services/categories-formations.service';
import { CategoriesOthersService } from '../categories/services/categories-others.service';
import { TempUserService } from '../temp-user/temp-user.service';
import { UsersService } from '../users/users.service';
import { UsersService } from '../users/services/users.service';
import { CreateStructureDto } from './dto/create-structure.dto';
import { StructuresService } from './services/structures.service';
import { StructuresController } from './structures.controller';
......@@ -91,6 +91,7 @@ describe('AuthController', () => {
deletedAt: null,
remoteAccompaniment: null,
dataShareConsentDate: null,
personalOffers: [],
...structure,
});
expect(res.structureName).toBe('a');
......@@ -132,22 +133,28 @@ describe('AuthController', () => {
pendingStructuresLink: null,
structuresLink: null,
structureOutdatedMailSent: null,
personalOffers: null,
email: user.email,
name: user.name,
surname: user.surname,
emailVerified: true,
createdAt: new Date('2022-05-25T09:48:28.824Z'),
password: user.password,
validationToken: null,
role: null,
employer: {
name: 'test',
validated: true,
},
job: {
name: 'test',
validated: true,
hasPersonalOffer: false,
},
});
expect(res).toBeTruthy();
});
it('should count', async () => {
const res = controller.countCategories([{ id: 'equipmentsAndServices', text: 'wifiEnAccesLibre' }]);
expect(res).toBeTruthy();
});
it('should search an address', async () => {
const res = controller.searchAddress({ searchQuery: 'Rue Alphonse Daudet' });
expect(res).toBeTruthy();
......@@ -188,6 +195,11 @@ describe('AuthController', () => {
expect(res).toBeTruthy();
});
it('should remove Owner', async () => {
const res = controller.removeTempUser('6093ba0e2ab5775cfc01ed3e', 'tsfsf6296');
expect(res).toBeTruthy();
});
it('should join user', async () => {
const userMock = new UsersServiceMock();
const user = userMock.findOne('pauline.dupont@mii.com');
......@@ -199,13 +211,24 @@ describe('AuthController', () => {
pendingStructuresLink: null,
structuresLink: null,
structureOutdatedMailSent: null,
personalOffers: null,
email: user.email,
name: user.name,
surname: user.surname,
emailVerified: true,
createdAt: new Date('2022-05-25T09:48:28.824Z'),
password: user.password,
validationToken: null,
role: null,
employer: {
name: 'test',
validated: true,
},
job: {
name: 'test',
validated: true,
hasPersonalOffer: false,
},
});
expect(res).toBeTruthy();
res = controller.join('', null);
......@@ -214,17 +237,8 @@ describe('AuthController', () => {
expect(res).toBeTruthy();
});
it('should join in struct', async () => {
let res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'true', 'tsfsf6296');
expect(res).toBeTruthy();
res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'true', '');
expect(res).toBeTruthy();
res = controller.joinValidation('', 'true', '');
expect(res).toBeTruthy();
});
it('should remove user from struct', async () => {
const res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'false', 'tsfsf6296');
const res = controller.removeOwner('6093ba0e2ab5775cfc01ed3e', 'tsfsf6296');
expect(res).toBeTruthy();
});
......
import { HttpService } from '@nestjs/axios';
import {
Body,
Controller,
Delete,
Get,
HttpException,
HttpService,
HttpStatus,
Logger,
Param,
Patch,
Post,
Put,
Query,
UseGuards,
} from '@nestjs/common';
import { ApiParam } from '@nestjs/swagger';
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { Types } from 'mongoose';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CategoriesAccompagnementService } from '../categories/services/categories-accompagnement.service';
......@@ -25,14 +26,15 @@ import { Roles } from '../users/decorators/roles.decorator';
import { IsStructureOwnerGuard } from '../users/guards/isStructureOwner.guard';
import { RolesGuard } from '../users/guards/roles.guard';
import { User } from '../users/schemas/user.schema';
import { UsersService } from '../users/users.service';
import { UsersService } from '../users/services/users.service';
import { depRegex } from './common/regex';
import { CreateStructureDto } from './dto/create-structure.dto';
import { QueryStructure } from './dto/query-structure.dto';
import { structureDto } from './dto/structure.dto';
import { UpdateStructureDto } from './dto/update-structure.dto';
import { Structure, StructureDocument } from './schemas/structure.schema';
import { StructuresService } from './services/structures.service';
@ApiTags('structures')
@Controller('structures')
export class StructuresController {
private readonly logger = new Logger(StructuresController.name);
......@@ -70,6 +72,7 @@ export class StructuresController {
@Post()
public async create(@Body() createStructureDto: CreateStructureDto): Promise<Structure> {
Logger.debug(`create | ${createStructureDto.structure.structureName}`, StructuresController.name);
return this.structureService.create(createStructureDto.idUser, createStructureDto.structure);
}
......@@ -78,6 +81,11 @@ export class StructuresController {
return this.structureService.searchForStructures(query.query, body ? body.filters : null);
}
@Post('searchByName')
public async searchByName(@Query() query: QueryStructure, @Body() body): Promise<Structure[]> {
return this.structureService.searchForStructures(query.query, body ? body.filters : null, ['structureName']);
}
@Post('resetSearchIndex')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
......@@ -87,13 +95,15 @@ export class StructuresController {
@Put('updateAfterOwnerVerify/:id')
public async updateAfterOwnerVerify(@Param('id') id: string): Promise<Structure> {
Logger.debug(`updateAfterOwnerVerify | structure ${id}`, StructuresController.name);
return this.structureService.updateAccountVerified(id);
}
@Put(':id')
@ApiOperation({ summary: 'Patch one value or more of a structure' })
@Patch(':id')
@UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
@Roles('admin')
public async update(@Param('id') id: string, @Body() body: structureDto): Promise<Structure> {
public async update(@Param('id') id: string, @Body() body: UpdateStructureDto): Promise<Structure> {
return this.structureService.update(id, body);
}
......@@ -122,30 +132,6 @@ export class StructuresController {
return this.userService.updateStructureLinkedClaim(user.email, idStructure, structure);
}
@Post('count')
public async countCategories(
@Body()
selectedFilter: { id: string; text: string }[]
): Promise<Array<{ id: string; count: number }>> {
const data = await Promise.all([
this.structureService.countByStructureKey('proceduresAccompaniment', selectedFilter),
this.structureService.countByStructureKey('accessRight', selectedFilter),
this.structureService.countByStructureKey('baseSkills', selectedFilter),
this.structureService.countByStructureKey('parentingHelp', selectedFilter),
this.structureService.countByStructureKey('digitalCultureSecurity', selectedFilter),
this.structureService.countByStructureKey('socialAndProfessional', selectedFilter),
this.structureService.countByStructureKey('publicsAccompaniment', selectedFilter),
this.structureService.countByStructureKey('labelsQualifications', selectedFilter),
this.structureService.countByStructureKey('publics', selectedFilter),
this.structureService.countByStructureKey('accessModality', selectedFilter),
this.structureService.countByStructureKey('equipmentsAndServices', selectedFilter),
]);
// Return a concat of all arrays
return data.reduce((a, b) => [...a, ...b]);
}
@Post('address')
public async searchAddress(@Body() data: { searchQuery: string }) {
return this.structureService.searchAddress(data);
......@@ -166,6 +152,11 @@ export class StructuresController {
return this.structureService.findWithOwners(id, data.emailUser);
}
@Get(':id/tempUsers')
public async getTempUsers(@Param('id') id: string) {
return this.tempUserService.getStructureTempUsers(id);
}
@Delete(':id')
@UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
@Roles('admin')
......@@ -205,73 +196,50 @@ export class StructuresController {
if (!structure) {
throw new HttpException('Invalid Structure', HttpStatus.NOT_FOUND);
}
// Get user and add pending structure
// Get user and add structure to user
const userFromDb = await this.userService.findOne(user.email);
if (!userFromDb) {
throw new HttpException('Invalid User', HttpStatus.NOT_FOUND);
}
// If user has not already request it, send owner's validation email
if (!userFromDb.pendingStructuresLink.includes(Types.ObjectId(id))) {
userFromDb.pendingStructuresLink.push(Types.ObjectId(id));
userFromDb.save();
// Send structure owner's an email
this.structureService.sendStructureJoinRequest(userFromDb, structure);
}
await this.userService.updateStructureLinked(userFromDb.email, id);
// Send structure owners an email
this.structureService.sendStructureJoinRequest(userFromDb, structure);
}
@Post(':id/join/:userId/:status')
@Delete(':id/owner/:userId')
@UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
@ApiParam({ name: 'id', type: String, required: true })
@ApiParam({ name: 'userId', type: String, required: true })
@ApiParam({ name: 'status', type: String, required: true })
public async joinValidation(
@Param('id') id: string,
@Param('status') status: string,
@Param('userId') userId: string
): Promise<any> {
// Get structure name
public async removeOwner(@Param('id') id: string, @Param('userId') userId: string): Promise<void> {
// Get structure
const structure = await this.structureService.findOne(id);
if (!structure) {
throw new HttpException('Invalid Structure', HttpStatus.NOT_FOUND);
}
// Get user and add pending structure
// Get user
const userFromDb = await this.userService.findById(userId);
if (!userFromDb) {
if (!userFromDb || !userFromDb.structuresLink.includes(Types.ObjectId(id))) {
throw new HttpException('Invalid User', HttpStatus.NOT_FOUND);
}
if (!userFromDb.pendingStructuresLink.includes(Types.ObjectId(id))) {
throw new HttpException('User not linked to structure', HttpStatus.NOT_FOUND);
}
if (status === 'true') {
// Accept
await this.userService.updateStructureLinked(userFromDb.email, id);
await this.userService.removeFromPendingStructureLinked(userFromDb.email, id);
} else {
// Refuse
this.userService.removeFromPendingStructureLinked(userFromDb.email, id);
}
this.userService.removeFromStructureLinked(userFromDb.email, id);
}
@Delete(':id/owner/:userId')
@Delete(':id/tempUser/:userId')
@UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
@Roles('admin')
@ApiParam({ name: 'id', type: String, required: true })
@ApiParam({ name: 'userId', type: String, required: true })
public async removeOwner(@Param('id') id: string, @Param('userId') userId: string): Promise<void> {
public async removeTempUser(@Param('id') id: string, @Param('userId') userId: string): Promise<void> {
// Get structure
const structure = await this.structureService.findOne(id);
if (!structure) {
throw new HttpException('Invalid Structure', HttpStatus.NOT_FOUND);
}
// Get user
const userFromDb = await this.userService.findById(userId);
if (!userFromDb || !userFromDb.structuresLink.includes(Types.ObjectId(id))) {
throw new HttpException('Invalid User', HttpStatus.NOT_FOUND);
// Get temp user
const userFromDb = await this.tempUserService.findById(userId);
if (!userFromDb || !userFromDb.pendingStructuresLink.includes(Types.ObjectId(id))) {
throw new HttpException('Invalid temp user', HttpStatus.NOT_FOUND);
}
this.userService.removeFromStructureLinked(userFromDb.email, id);
this.tempUserService.removeFromStructureLinked(userFromDb.email, id);
}
@Post('reportStructureError')
......
import { forwardRef, HttpModule, Module } from '@nestjs/common';
import { forwardRef, Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { MongooseModule } from '@nestjs/mongoose';
import { TempUserModule } from '../temp-user/temp-user.module';
import { MailerModule } from '../mailer/mailer.module';
......
import { HttpModule, Module } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { TclStopPointService } from './tclStopPoint.service';
import { TclStopPointController } from './tclStopPoint.controller';
import { MongooseModule } from '@nestjs/mongoose';
......
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { Roles } from '../users/decorators/roles.decorator';
import { RolesGuard } from '../users/guards/roles.guard';
......@@ -7,6 +7,7 @@ import { PgisCoord } from './interfaces/pgis.coord';
import { TclStopPoint } from './tclStopPoint.schema';
import { TclStopPointService } from './tclStopPoint.service';
@ApiTags('tcl')
@Controller('tcl')
export class TclStopPointController {
constructor(private tclStopPointService: TclStopPointService) {}
......
import { HttpService, Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { PgisCoord } from './interfaces/pgis.coord';
......@@ -118,8 +119,7 @@ export class TclStopPointService {
tramLines: [],
};
for (let line of receivedLinesArray) {
line = line.split(':')[0];
for (const line of receivedLinesArray) {
let cleanLine: string;
let lineType: string[];
......