Skip to content

Commit d06d03c

Browse files
Merge branch 'main' into foreign-key-widget
2 parents 0b6b2ad + 3b89e40 commit d06d03c

File tree

3 files changed

+171
-15
lines changed

3 files changed

+171
-15
lines changed

backend/test/ava-tests/saas-tests/company-info-e2e.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ test.serial(`${currentTest} should return found company info for user`, async (t
8181

8282
t.is(foundCompanyInfo.status, 200);
8383
const foundCompanyInfoRO = JSON.parse(foundCompanyInfo.text);
84-
t.is(Object.keys(foundCompanyInfoRO).length, 7);
84+
t.is(Object.keys(foundCompanyInfoRO).length, 8);
8585
t.is(foundCompanyInfoRO.hasOwnProperty('id'), true);
8686
t.is(foundCompanyInfoRO.hasOwnProperty('name'), true);
8787
t.is(foundCompanyInfoRO.hasOwnProperty('additional_info'), true);
@@ -174,7 +174,7 @@ test.serial(`${currentTest} should return found company info for non-admin user`
174174
const foundCompanyInfoRO = JSON.parse(foundCompanyInfo.text);
175175

176176
t.is(foundCompanyInfo.status, 200);
177-
t.is(Object.keys(foundCompanyInfoRO).length, 7);
177+
t.is(Object.keys(foundCompanyInfoRO).length, 8);
178178
t.is(foundCompanyInfoRO.hasOwnProperty('id'), true);
179179
t.is(foundCompanyInfoRO.hasOwnProperty('name'), true);
180180
t.is(foundCompanyInfoRO.hasOwnProperty('additional_info'), true);

backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts

Lines changed: 139 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,27 @@ import { faker } from '@faker-js/faker';
33
import { INestApplication, ValidationPipe } from '@nestjs/common';
44
import { Test } from '@nestjs/testing';
55
import test from 'ava';
6+
import { ValidationError } from 'class-validator';
67
import cookieParser from 'cookie-parser';
8+
import JSON5 from 'json5';
9+
import { MongoClient } from 'mongodb';
710
import request from 'supertest';
811
import { ApplicationModule } from '../../../src/app.module.js';
12+
import { CreateOrUpdateTableWidgetsDto } from '../../../src/entities/widget/dto/create-table-widget.dto.js';
13+
import { WidgetTypeEnum } from '../../../src/enums/widget-type.enum.js';
914
import { AllExceptionsFilter } from '../../../src/exceptions/all-exceptions.filter.js';
15+
import { ValidationException } from '../../../src/exceptions/custom-exceptions/validation-exception.js';
1016
import { Messages } from '../../../src/exceptions/text/messages.js';
1117
import { Cacher } from '../../../src/helpers/cache/cacher.js';
1218
import { DatabaseModule } from '../../../src/shared/database/database.module.js';
1319
import { DatabaseService } from '../../../src/shared/database/database.service.js';
1420
import { MockFactory } from '../../mock.factory.js';
1521
import { compareTableWidgetsArrays } from '../../utils/compare-table-widgets-arrays.js';
22+
import { createTestTable } from '../../utils/create-test-table.js';
1623
import { getTestData } from '../../utils/get-test-data.js';
24+
import { getTestKnex } from '../../utils/get-test-knex.js';
1725
import { registerUserAndReturnUserInfo } from '../../utils/register-user-and-return-user-info.js';
1826
import { TestUtils } from '../../utils/test.utils.js';
19-
import { ValidationException } from '../../../src/exceptions/custom-exceptions/validation-exception.js';
20-
import { ValidationError } from 'class-validator';
21-
import { CreateOrUpdateTableWidgetsDto } from '../../../src/entities/widget/dto/create-table-widget.dto.js';
22-
import { WidgetTypeEnum } from '../../../src/enums/widget-type.enum.js';
23-
import { createTestTable } from '../../utils/create-test-table.js';
24-
import { getTestKnex } from '../../utils/get-test-knex.js';
25-
import JSON5 from 'json5';
2627

2728
const mockFactory = new MockFactory();
2829
let app: INestApplication;
@@ -782,3 +783,134 @@ test.serial(`${currentTest} should return created table widgets`, async (t) => {
782783
t.is(getTableStructureRO.foreignKeys[0].hasOwnProperty('autocomplete_columns'), true);
783784
t.is(getTableStructureRO.foreignKeys[0].autocomplete_columns.length, 5);
784785
});
786+
787+
// Table widgets for mongodb database
788+
test.serial(
789+
`${currentTest} should return created table widgets as foreign keys, when database is mongodb`,
790+
async (t) => {
791+
const connectionToTestDB = getTestData(mockFactory).mongoDbConnection;
792+
const { token } = await registerUserAndReturnUserInfo(app);
793+
794+
const mongoConnectionString =
795+
`mongodb://${connectionToTestDB.username}` +
796+
`:${connectionToTestDB.password}` +
797+
`@${connectionToTestDB.host}` +
798+
`:${connectionToTestDB.port}` +
799+
`/${connectionToTestDB.database}`;
800+
801+
const referencedOnTableTableName = `users`;
802+
const referencedByTableName = `orders`;
803+
const testTableColumnName = `user_name`;
804+
const testReferencedColumnsName = `product_description`;
805+
const referencedByColumnName = 'user_id';
806+
const referencedOnColumnName = '_id';
807+
808+
const client = new MongoClient(mongoConnectionString);
809+
await client.connect();
810+
const db = client.db(connectionToTestDB.database);
811+
const referencedCollection = db.collection(referencedOnTableTableName);
812+
const referencedByCollection = db.collection(referencedByTableName);
813+
814+
await referencedCollection.drop();
815+
await referencedByCollection.drop();
816+
817+
const insertedReferencedIds = [];
818+
for (let index = 0; index < 42; index++) {
819+
const insertedReferencedId = await referencedCollection.insertOne({
820+
[testTableColumnName]: faker.person.fullName(),
821+
created_at: new Date(),
822+
updated_at: new Date(),
823+
});
824+
insertedReferencedIds.push(insertedReferencedId.insertedId.toHexString());
825+
}
826+
827+
for (let index = 0; index < 42; index++) {
828+
await referencedByCollection.insertOne({
829+
[referencedByColumnName]: insertedReferencedIds[index],
830+
[testReferencedColumnsName]: faker.lorem.lines(),
831+
created_at: new Date(),
832+
updated_at: new Date(),
833+
});
834+
}
835+
836+
const foreignKeyWidgetsDTO: CreateOrUpdateTableWidgetsDto = {
837+
widgets: [
838+
{
839+
widget_type: WidgetTypeEnum.Foreign_key,
840+
widget_params: JSON.stringify({
841+
referenced_column_name: referencedOnColumnName,
842+
referenced_table_name: referencedOnTableTableName,
843+
constraint_name: 'manually_created_constraint',
844+
column_name: referencedByColumnName,
845+
}),
846+
field_name: referencedByColumnName,
847+
description: 'User ID as foreign key',
848+
name: 'User ID',
849+
widget_options: JSON.stringify({}),
850+
},
851+
],
852+
};
853+
854+
const createConnectionResponse = await request(app.getHttpServer())
855+
.post('/connection')
856+
.send(connectionToTestDB)
857+
.set('Cookie', token)
858+
.set('Content-Type', 'application/json')
859+
.set('Accept', 'application/json');
860+
const createConnectionRO = JSON.parse(createConnectionResponse.text);
861+
t.is(createConnectionResponse.status, 201);
862+
const connectionId = createConnectionRO.id;
863+
864+
const createTableWidgetResponse = await request(app.getHttpServer())
865+
.post(`/widget/${connectionId}?tableName=${referencedByTableName}`)
866+
.send(foreignKeyWidgetsDTO)
867+
.set('Content-Type', 'application/json')
868+
.set('Cookie', token)
869+
.set('Accept', 'application/json');
870+
const createTableWidgetRO = JSON.parse(createTableWidgetResponse.text);
871+
console.log('🚀 ~ createTableWidgetRO:', createTableWidgetRO);
872+
t.is(createTableWidgetResponse.status, 201);
873+
874+
const getTableWidgets = await request(app.getHttpServer())
875+
.get(`/widgets/${connectionId}?tableName=${referencedByTableName}`)
876+
.set('Content-Type', 'application/json')
877+
.set('Cookie', token)
878+
.set('Accept', 'application/json');
879+
t.is(getTableWidgets.status, 200);
880+
const getTableWidgetsRO = JSON.parse(getTableWidgets.text);
881+
t.is(typeof getTableWidgetsRO, 'object');
882+
t.is(getTableWidgetsRO.length, 1);
883+
884+
t.is(getTableWidgetsRO[0].widget_type, foreignKeyWidgetsDTO.widgets[0].widget_type);
885+
886+
const getTableStructureResponse = await request(app.getHttpServer())
887+
.get(`/table/structure/${connectionId}?tableName=${referencedByTableName}`)
888+
.set('Content-Type', 'application/json')
889+
.set('Cookie', token)
890+
.set('Accept', 'application/json');
891+
892+
const getTableStructureRO = JSON.parse(getTableStructureResponse.text);
893+
894+
t.is(getTableStructureResponse.status, 200);
895+
t.is(getTableStructureRO.hasOwnProperty('table_widgets'), true);
896+
t.is(getTableStructureRO.table_widgets.length, 1);
897+
t.is(getTableStructureRO.table_widgets[0].field_name, foreignKeyWidgetsDTO.widgets[0].field_name);
898+
t.is(getTableStructureRO.table_widgets[0].widget_type, foreignKeyWidgetsDTO.widgets[0].widget_type);
899+
t.is(getTableStructureRO.hasOwnProperty('foreignKeys'), true);
900+
t.is(getTableStructureRO.foreignKeys.length, 1);
901+
t.is(getTableStructureRO.foreignKeys[0].column_name, foreignKeyWidgetsDTO.widgets[0].field_name);
902+
t.is(getTableStructureRO.foreignKeys[0].referenced_table_name, referencedOnTableTableName);
903+
904+
// check table rows received with foreign keys from widget
905+
906+
const getRowsResponse = await request(app.getHttpServer())
907+
.get(`/table/rows/${connectionId}?tableName=${referencedByTableName}`)
908+
.set('Content-Type', 'application/json')
909+
.set('Cookie', token)
910+
.set('Accept', 'application/json');
911+
t.is(getRowsResponse.status, 200);
912+
const getRowsRO = JSON.parse(getRowsResponse.text);
913+
t.is(typeof getRowsRO.rows[0], 'object');
914+
t.is(getRowsRO.rows[0].hasOwnProperty('_id'), true);
915+
},
916+
);

shared-code/src/data-access-layer/data-access-objects/data-access-object-mongodb.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,36 @@ export class DataAccessObjectMongo extends BasicDataAccessObject implements IDat
5959
}
6060

6161
public async getIdentityColumns(
62-
_tableName: string,
63-
_referencedFieldName: string,
64-
_identityColumnName: string,
65-
_fieldValues: (string | number)[],
62+
tableName: string,
63+
referencedFieldName: string,
64+
identityColumnName: string,
65+
fieldValues: (string | number)[],
6666
): Promise<Record<string, unknown>[]> {
67-
return [];
67+
if (!referencedFieldName || !fieldValues.length) {
68+
return [];
69+
}
70+
71+
const db = await this.getConnectionToDatabase();
72+
const collection = db.collection(tableName);
73+
74+
if (referencedFieldName === '_id') {
75+
fieldValues = fieldValues.map((value) => this.createObjectIdFromSting(String(value))) as any;
76+
}
77+
78+
const query = {
79+
[referencedFieldName]: { $in: fieldValues },
80+
};
81+
82+
const projection = identityColumnName
83+
? { [identityColumnName]: 1, [referencedFieldName]: 1 }
84+
: { [referencedFieldName]: 1 };
85+
86+
const results = await collection.find(query).project(projection).toArray();
87+
88+
return results.map((doc) => ({
89+
...doc,
90+
_id: this.processMongoIdField(doc._id),
91+
}));
6892
}
6993

7094
public async getRowByPrimaryKey(
@@ -375,7 +399,7 @@ export class DataAccessObjectMongo extends BasicDataAccessObject implements IDat
375399
const aggregationPipeline = JSON.parse(query);
376400
const result = await collection.aggregate(aggregationPipeline).toArray();
377401
return result;
378-
}
402+
}
379403

380404
private async getConnectionToDatabase(): Promise<Db> {
381405
if (this.connection.ssh) {

0 commit comments

Comments
 (0)