Skip to content

Commit

Permalink
v1.141.0
Browse files Browse the repository at this point in the history
  • Loading branch information
varovaro committed Nov 6, 2023
2 parents 6ca2027 + 749ab35 commit 58c15d6
Show file tree
Hide file tree
Showing 84 changed files with 1,159 additions and 804 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci_e2e_cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ jobs:
run: sleep 5 && wget --waitretry=5 --retry-connrefused -v http://localhost:3000/
timeout-minutes: 2
- name: Cypress run
uses: cypress-io/github-action@v5
uses: cypress-io/github-action@v6
with:
browser: chrome
component: false
config: defaultCommandTimeout=8000,requestTimeout=12000
config-file: cypress.config.ts
timeout-minutes: 25
env:
DBHOST: localhost:27017
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Some suites need MongoDB configured in Replica Set mode to run properly. The pro

#### End to End (e2e)

For End-to-End testing, we have a full set of fixtures that test the overall functionality. Be advised that, for the time being, these tests are run ON THE SAME DATABASE as the default database (uwazi_developmet), so running these tests will DELETE any exisisting data and replace it with the testing fixtures. DO NOT RUN ON PRODUCTION ENVIRONMENTS!
For End-to-End testing, we have a full set of fixtures that test the overall functionality. Be advised that, for the time being, these tests are run ON THE SAME DATABASE as the default database (uwazi_developmet), so running these tests will DELETE any existing data and replace it with the testing fixtures. DO NOT RUN ON PRODUCTION ENVIRONMENTS!

Running end to end tests require a running Uwazi app.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Db } from 'mongodb';

import { MongoTransactionManager } from 'api/common.v2/database/MongoTransactionManager';
import { getClient } from 'api/common.v2/database/getConnectionForCurrentTenant';
import { MongoResultSet } from 'api/common.v2/database/MongoResultSet';
import { getFixturesFactory } from 'api/utils/fixturesFactory';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import testingDB, { DBFixture } from 'api/utils/testing_db';

import { DefaultTransactionManager } from 'api/common.v2/database/data_source_defaults';
import { MongoPermissionsDataSource } from '../MongoPermissionsDataSource';

const factory = getFixturesFactory();
Expand Down Expand Up @@ -82,10 +81,7 @@ describe('MongoPermissionsDataSource', () => {
])(
'should return the permissions for entities with the given sharedIds',
async ({ sharedIds, expected }) => {
const dataSource = new MongoPermissionsDataSource(
db!,
new MongoTransactionManager(getClient())
);
const dataSource = new MongoPermissionsDataSource(db!, DefaultTransactionManager());
const resultSet = dataSource.getByEntities(sharedIds);
expect(resultSet).toBeInstanceOf(MongoResultSet);
const result = await resultSet.all();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { MongoPermissionsDataSource } from 'api/authorization.v2/database/MongoPermissionsDataSource';
import { UnauthorizedError } from 'api/authorization.v2/errors/UnauthorizedError';
import { getClient, getConnection } from 'api/common.v2/database/getConnectionForCurrentTenant';
import { MongoTransactionManager } from 'api/common.v2/database/MongoTransactionManager';
import { getConnection } from 'api/common.v2/database/getConnectionForCurrentTenant';
import { Relationship } from 'api/relationships.v2/model/Relationship';
import { MongoRelationshipsDataSource } from 'api/relationships.v2/database/MongoRelationshipsDataSource';
import { User, UserRole } from 'api/users.v2/model/User';
import { getFixturesFactory } from 'api/utils/fixturesFactory';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import { DBFixture } from 'api/utils/testing_db';
import { DefaultTransactionManager } from 'api/common.v2/database/data_source_defaults';
import { AccessLevels, AuthorizationService } from '../AuthorizationService';

const factory = getFixturesFactory();
Expand Down Expand Up @@ -58,7 +58,7 @@ describe("When there's no authenticated user", () => {
describe('and the entity is not public', () => {
it('should return false', async () => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
undefined
);
expect(await auth.isAuthorized('read', ['entity1'])).toBe(false);
Expand All @@ -67,7 +67,7 @@ describe("When there's no authenticated user", () => {

it('should throw an error on validation', async () => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
undefined
);
await expect(async () => auth.validateAccess('read', ['entity1'])).rejects.toThrow(
Expand All @@ -82,7 +82,7 @@ describe("When there's no authenticated user", () => {
describe('and the entity is public', () => {
it('should only allow to read', async () => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
undefined
);
expect(await auth.isAuthorized('read', ['entity3'])).toBe(true);
Expand All @@ -93,7 +93,7 @@ describe("When there's no authenticated user", () => {
describe('and not all the entities are public', () => {
it('should not allow to read nor write', async () => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
undefined
);
expect(await auth.isAuthorized('read', ['entity3', 'entity1'])).toBe(false);
Expand All @@ -103,7 +103,7 @@ describe("When there's no authenticated user", () => {

it('should allow empty read', async () => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
undefined
);
expect(await auth.isAuthorized('read', [])).toBe(true);
Expand All @@ -116,7 +116,7 @@ describe("When there's an authenticated user", () => {
it('should return true', async () => {
const adminUser = new User(factory.id('admin').toHexString(), role, []);
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
adminUser
);
expect(await auth.isAuthorized('read', ['entity1'])).toBe(true);
Expand All @@ -128,7 +128,7 @@ describe("When there's an authenticated user", () => {
it('should not throw an error', async () => {
const adminUser = new User(factory.id('admin').toHexString(), role, []);
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
adminUser
);

Expand Down Expand Up @@ -161,7 +161,7 @@ describe("When there's an authenticated user", () => {
'should return [$result] if [$user] wants to [$level] from/to $entities',
async ({ user, entities, level, result }) => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
new User(factory.id(user).toHexString(), 'collaborator', [])
);

Expand All @@ -175,7 +175,7 @@ describe("When there's an authenticated user", () => {
{ entities: ['entity3'], level: 'write', result: true },
])('should consider the user groups', async ({ entities, level, result }) => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
new User(factory.id('grouped user').toHexString(), 'collaborator', [
factory.id('group1').toHexString(),
factory.id('group2').toHexString(),
Expand Down Expand Up @@ -246,7 +246,7 @@ describe('filterEntities()', () => {
'should filter entities for a/an $usertype to $level',
async ({ user, level, expectedResult }) => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
user
);

Expand All @@ -261,10 +261,7 @@ describe('filterRelationships()', () => {
let allRelationships: Relationship[];

beforeAll(async () => {
const ds = new MongoRelationshipsDataSource(
getConnection(),
new MongoTransactionManager(getClient())
);
const ds = new MongoRelationshipsDataSource(getConnection(), DefaultTransactionManager());
allRelationships = await ds.getAll().all();
});

Expand Down Expand Up @@ -300,7 +297,7 @@ describe('filterRelationships()', () => {
},
])('should filter for $case', async ({ user, level, expectedIds }) => {
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
user
);

Expand All @@ -321,7 +318,7 @@ describe('filterRelationships()', () => {
])('$role should be able to $level everything', async ({ role: username, level }) => {
const user = new User(factory.idString(username), username, []);
const auth = new AuthorizationService(
new MongoPermissionsDataSource(getConnection(), new MongoTransactionManager(getClient())),
new MongoPermissionsDataSource(getConnection(), DefaultTransactionManager()),
user
);

Expand Down
10 changes: 8 additions & 2 deletions app/api/common.v2/database/MongoTransactionManager.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { MongoClient, ClientSession } from 'mongodb';
import { Logger } from 'api/log.v2/contracts/Logger';
import { TransactionManager } from '../contracts/TransactionManager';

export class MongoTransactionManager implements TransactionManager {
private mongoClient: MongoClient;

private logger: Logger;

private session?: ClientSession;

private onCommitHandlers: ((returnValue: any) => Promise<void>)[];

private finished = false;

constructor(mongoClient: MongoClient) {
constructor(mongoClient: MongoClient, logger: Logger) {
this.onCommitHandlers = [];
this.mongoClient = mongoClient;
this.logger = logger;
}

async executeOnCommitHandlers(returnValue: unknown) {
Expand All @@ -35,6 +39,7 @@ export class MongoTransactionManager implements TransactionManager {
this.finished = true;
} catch (error) {
if (error.hasErrorLabel && error.hasErrorLabel('UnknownTransactionCommitResult')) {
this.logger.debug(error);
await this.commitWithRetry();
} else {
throw error;
Expand All @@ -57,7 +62,7 @@ export class MongoTransactionManager implements TransactionManager {
await this.commitWithRetry();
return returnValue;
} catch (error) {
if (error.code !== 251) {
if (this.session?.inTransaction()) {
await this.abortTransaction();
}

Expand All @@ -70,6 +75,7 @@ export class MongoTransactionManager implements TransactionManager {
return await this.runInTransaction(callback);
} catch (error) {
if (retries > 0 && error.hasErrorLabel && error.hasErrorLabel('TransientTransactionError')) {
this.logger.debug(error);
return this.runWithRetry(callback, retries - 1);
}

Expand Down
4 changes: 3 additions & 1 deletion app/api/common.v2/database/data_source_defaults.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { DefaultLogger } from 'api/log.v2/infrastructure/StandardLogger';
import { IdGenerator } from '../contracts/IdGenerator';
import { getClient } from './getConnectionForCurrentTenant';
import { MongoIdHandler } from './MongoIdGenerator';
import { MongoTransactionManager } from './MongoTransactionManager';

const DefaultTransactionManager = () => {
const client = getClient();
return new MongoTransactionManager(client);
const logger = DefaultLogger();
return new MongoTransactionManager(client, logger);
};

const DefaultIdGenerator: IdGenerator = MongoIdHandler;
Expand Down
14 changes: 7 additions & 7 deletions app/api/common.v2/database/specs/MongoDataSource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import testingDB from 'api/utils/testing_db';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import { ObjectId } from 'mongodb';
import { MongoDataSource } from '../MongoDataSource';
import { MongoTransactionManager } from '../MongoTransactionManager';
import { getClient, getConnection } from '../getConnectionForCurrentTenant';
import { getConnection } from '../getConnectionForCurrentTenant';
import { DefaultTransactionManager } from '../data_source_defaults';

const blankState = [
{
Expand Down Expand Up @@ -133,7 +133,7 @@ describe('session scoped collection', () => {
it.each(casesForUpdates)(
'$method should write changes transactionally',
async ({ callback, expectedOnAbort, expectedOnSuccess }) => {
const transactionManager1 = new MongoTransactionManager(getClient());
const transactionManager1 = DefaultTransactionManager();
const dataSource1 = new DataSource(getConnection(), transactionManager1);

try {
Expand All @@ -150,7 +150,7 @@ describe('session scoped collection', () => {
);
}

const transactionManager2 = new MongoTransactionManager(getClient());
const transactionManager2 = DefaultTransactionManager();
const dataSource2 = new DataSource(getConnection(), transactionManager2);

await transactionManager2.run(async () => {
Expand Down Expand Up @@ -203,7 +203,7 @@ describe('session scoped collection', () => {
it.each(casesForReads)(
'$method should read data from the transaction',
async ({ callback, expectedInTransaction }) => {
const transactionManager1 = new MongoTransactionManager(getClient());
const transactionManager1 = DefaultTransactionManager();
const dataSource1 = new DataSource(getConnection(), transactionManager1);

let result;
Expand Down Expand Up @@ -238,7 +238,7 @@ describe('session scoped collection', () => {
it.each(otherCases)(
'$method should return the information according to the transaction state',
async ({ callback, expectedInTransaction, expectedNoTransaction }) => {
const transactionManager1 = new MongoTransactionManager(getClient());
const transactionManager1 = DefaultTransactionManager();
const dataSource1 = new DataSource(getConnection(), transactionManager1);

let result;
Expand All @@ -254,7 +254,7 @@ describe('session scoped collection', () => {
expect(result).toEqual(expectedInTransaction);
}

const transactionManager2 = new MongoTransactionManager(getClient());
const transactionManager2 = DefaultTransactionManager();
const dataSource2 = new DataSource(getConnection(), transactionManager2);

expect(await callback(dataSource2)).toEqual(expectedNoTransaction);
Expand Down
8 changes: 4 additions & 4 deletions app/api/common.v2/database/specs/MongoDataSourceSync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { getIdMapper } from 'api/utils/fixturesFactory';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import testingDB from 'api/utils/testing_db';
import { ObjectId } from 'mongodb';
import { getClient, getConnection } from '../getConnectionForCurrentTenant';
import { getConnection } from '../getConnectionForCurrentTenant';
import { MongoDataSource } from '../MongoDataSource';
import { MongoTransactionManager } from '../MongoTransactionManager';
import { DefaultTransactionManager } from '../data_source_defaults';

const id = getIdMapper();

Expand Down Expand Up @@ -642,7 +642,7 @@ describe('collection with automatic log to updatelogs', () => {
expectedResult,
expectedDBStateOnTransactionError = updateLogsBlankState,
}) => {
const transactionManager1 = new MongoTransactionManager(getClient());
const transactionManager1 = DefaultTransactionManager();
const dataSource1 = new DataSource(getConnection(), transactionManager1);

try {
Expand All @@ -659,7 +659,7 @@ describe('collection with automatic log to updatelogs', () => {
);
}

const transactionManager2 = new MongoTransactionManager(getClient());
const transactionManager2 = DefaultTransactionManager();
const dataSource2 = new DataSource(getConnection(), transactionManager2);

const result = await transactionManager2.run(async () => callback(dataSource2));
Expand Down
Loading

0 comments on commit 58c15d6

Please sign in to comment.