Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release'
Browse files Browse the repository at this point in the history
  • Loading branch information
daneryl committed Jan 25, 2021
2 parents 20dbd3f + 785addc commit 4407b56
Show file tree
Hide file tree
Showing 26 changed files with 266 additions and 108 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/ci_e2e_puppeteer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ jobs:
ports:
- 27017/tcp
steps:
- uses: actions/checkout@v2
- name: Checkout reposistory
uses: actions/checkout@v2
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Use Node.js 14.6.x
uses: actions/setup-node@v1
with:
Expand Down Expand Up @@ -51,11 +54,6 @@ jobs:
- name: build production
if: steps.cache-build.outputs.cache-hit != 'true'
run: yarn production-build
- name: checkout fixtures
uses: actions/checkout@v2
with:
repository: huridocs/uwazi-fixtures
path: uwazi-fixtures
- run: yarn e2e-puppeteer-fixtures
env:
DBHOST: localhost:${{ job.services.mongodb.ports[27017] }}
Expand Down
12 changes: 11 additions & 1 deletion app/api/migrations/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConnectionOptions } from 'mongoose';
import { DB } from 'api/odm';
import { tenants } from 'api/tenants/tenantContext';
import { config } from 'api/config';
Expand All @@ -8,8 +9,17 @@ process.on('unhandledRejection', error => {
throw error;
});

let auth: ConnectionOptions;

if (process.env.DBUSER) {
auth = {
user: process.env.DBUSER,
pass: process.env.DBPASS,
};
}

const run = async () => {
await DB.connect();
await DB.connect(config.DBHOST, auth);
const { db } = await DB.connectionForDB(config.defaultTenant.dbName);

await tenants.run(async () => {
Expand Down
9 changes: 2 additions & 7 deletions app/api/odm/DB.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import mongoose, { Connection } from 'mongoose';
import mongoose, { Connection, ConnectionOptions } from 'mongoose';
import { config } from 'api/config';

type dbAuth = {
user: string;
pass: string;
};

let connection: Connection;

// setting this on createConnection directly is not working, maybe mongoose bug?
mongoose.set('useCreateIndex', true);

const DB = {
async connect(uri: string = config.DBHOST, auth?: dbAuth) {
async connect(uri: string = config.DBHOST, auth?: ConnectionOptions) {
connection = await mongoose.createConnection(uri, {
...auth,
useUnifiedTopology: true,
Expand Down
2 changes: 2 additions & 0 deletions app/api/socketio/specs/socketClusterMode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { multitenantMiddleware } from 'api/utils/multitenantMiddleware';
import { tenants, Tenant } from 'api/tenants/tenantContext';

import { setupSockets } from '../setupSockets';
import { appContextMiddleware } from 'api/utils/appContextMiddleware';

const closeServer = async (httpServer: Server) =>
new Promise(resolve => {
Expand Down Expand Up @@ -55,6 +56,7 @@ const app: Application = express();
describe('socket middlewares setup', () => {
beforeAll(async () => {
server = await createServer(app, port);
app.use(appContextMiddleware);
app.use(multitenantMiddleware);
setupSockets(server, app);

Expand Down
2 changes: 2 additions & 0 deletions app/api/sync/specs/uploadRoute.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { testingTenants } from 'api/utils/testingTenants';
import { multitenantMiddleware } from 'api/utils/multitenantMiddleware';

import syncRoutes from '../routes';
import { appContextMiddleware } from 'api/utils/appContextMiddleware';

jest.mock(
'../../auth/authMiddleware.ts',
Expand All @@ -33,6 +34,7 @@ describe('sync', () => {
await deleteFile(uploadsPath('testUpload.txt'));

const app = express();
app.use(appContextMiddleware);
app.use(multitenantMiddleware);
syncRoutes(app);

Expand Down
21 changes: 11 additions & 10 deletions app/api/tenants/tenantContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AsyncLocalStorage } from 'async_hooks';
import { config } from 'api/config';
import handleError from 'api/utils/handleError.js';
import { appContext } from 'api/utils/AppContext';
import { TenantsModel } from './tenantsModel';

export type Tenant = {
Expand All @@ -14,8 +14,6 @@ export type Tenant = {
};

class Tenants {
storage = new AsyncLocalStorage<string>();

tenants: { [k: string]: Tenant };

constructor() {
Expand All @@ -40,21 +38,24 @@ class Tenants {
});
}

/**
* This is a proxy to the context run method using only the tenant information.
* It is here for backwards compatibility after refactoring.
* @param cb The callback to run in the context
* @param tenantName Tenant name
*/
// eslint-disable-next-line class-methods-use-this
async run(
cb: () => Promise<void>,
tenantName: string = config.defaultTenant.name
): Promise<void> {
return new Promise((resolve, reject) => {
this.storage.run(tenantName, () => {
cb()
.then(resolve)
.catch(reject);
});
return appContext.run(cb, {
tenant: tenantName,
});
}

current() {
const tenantName = this.storage.getStore();
const tenantName = <string>appContext.get('tenant');

if (!tenantName) {
throw new Error('There is no tenant on the current async context');
Expand Down
37 changes: 37 additions & 0 deletions app/api/utils/AppContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { AsyncLocalStorage } from 'async_hooks';

interface ContextData {
[k: string]: unknown;
}

class AppContext {
private storage = new AsyncLocalStorage<ContextData>();

private getContextObject() {
const data = this.storage.getStore();
if (!data) throw new Error('Accessing nonexistent async context');
return data;
}

async run(cb: () => Promise<void>, data: ContextData = {}): Promise<void> {
return new Promise((resolve, reject) => {
this.storage.run(data, () => {
cb()
.then(resolve)
.catch(reject);
});
});
}

get(key: string) {
return this.getContextObject()[key];
}

set(key: string, value: unknown) {
this.getContextObject()[key] = value;
}
}

const appContext = new AppContext();

export { appContext };
12 changes: 12 additions & 0 deletions app/api/utils/appContextMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Request, Response, NextFunction } from 'express';
import { appContext } from 'api/utils/AppContext';

const appContextMiddleware = (_req: Request, _res: Response, next: NextFunction) => {
appContext
.run(async () => {
next();
})
.catch(next);
};

export { appContextMiddleware };
10 changes: 4 additions & 6 deletions app/api/utils/multitenantMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Request, Response, NextFunction } from 'express';
import { tenants } from 'api/tenants/tenantContext';
import { appContext } from 'api/utils/AppContext';
import { config } from 'api/config';

const multitenantMiddleware = (req: Request, _res: Response, next: NextFunction) => {
tenants
.run(async () => {
next();
}, req.get('tenant'))
.catch(next);
appContext.set('tenant', req.get('tenant') || config.defaultTenant.name);
next();
};

export { multitenantMiddleware };
66 changes: 66 additions & 0 deletions app/api/utils/specs/appContext.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { appContext } from '../AppContext';

describe('appContext', () => {
describe('when running the callback inside a context', () => {
it('it should access the given values', async () => {
await appContext.run(
async () => {
expect(appContext.get('key1')).toBe('value1');
expect(appContext.get('key2')).toBe('value2');
},
{
key1: 'value1',
key2: 'value2',
}
);
});

it('it should return undefined if accessing an unexising key', async () => {
await appContext.run(
async () => {
expect(appContext.get('non-existing')).toBe(undefined);
},
{
key: 'value',
}
);
});

it('it should set a new key', async () => {
await appContext.run(async () => {
expect(appContext.get('someKey')).toBe(undefined);
appContext.set('someKey', 'someValue');
expect(appContext.get('someKey')).toBe('someValue');
});
});

it('it should overwrite existing keys', async () => {
await appContext.run(
async () => {
expect(appContext.get('someKey')).toBe('previous');
appContext.set('someKey', 'someValue');
expect(appContext.get('someKey')).toBe('someValue');
},
{
someKey: 'previous',
}
);
});
});

describe('when outside a context', () => {
const error = new Error('Accessing nonexistent async context');

it('should throw on get', () => {
expect(() => {
appContext.get('somKey');
}).toThrow(error);
});

it('should throw on set', () => {
expect(() => {
appContext.set('somKey', 'someValue');
}).toThrow(error);
});
});
});
31 changes: 31 additions & 0 deletions app/api/utils/specs/appContextMiddleware.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import request from 'supertest';
import express, { Application, Request, Response, NextFunction } from 'express';
import { appContextMiddleware } from '../appContextMiddleware';
import { appContext } from '../AppContext';

const testingRoutes = (app: Application) => {
app.get('/api/testGET', (_req, res, next) => {
res.json(appContext.get('someKey'));
next();
});
};

const helperMiddleware = (req: Request, _res: Response, next: NextFunction) => {
appContext.set('someKey', req.get('someHeader'));
next();
};

describe('appcontext middleware', () => {
it('should execute next middlewares inside an async context', async () => {
const app: Application = express();
app.use(appContextMiddleware);
app.use(helperMiddleware);
testingRoutes(app);

const response = await request(app)
.get('/api/testGET')
.set('someHeader', 'test');

expect(response.text).toBe(JSON.stringify('test'));
});
});
2 changes: 2 additions & 0 deletions app/api/utils/specs/multitenantMiddleware.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import request from 'supertest';
import express, { Application } from 'express';
import { tenants } from 'api/tenants/tenantContext';
import { multitenantMiddleware } from '../multitenantMiddleware';
import { appContextMiddleware } from '../appContextMiddleware';

const testingRoutes = (app: Application) => {
app.get('/api/testGET', (_req, res, next) => {
Expand All @@ -16,6 +17,7 @@ describe('multitenant middleware', () => {
tenants.add({ name: 'test' });

const app: Application = express();
app.use(appContextMiddleware);
app.use(multitenantMiddleware);
testingRoutes(app);

Expand Down
3 changes: 3 additions & 0 deletions app/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import path from 'path';

import { TaskProvider } from 'shared/tasks/tasks';

import { appContextMiddleware } from 'api/utils/appContextMiddleware';
import uwaziMessage from '../message';
import apiRoutes from './api/api';
import privateInstanceMiddleware from './api/auth/privateInstanceMiddleware';
Expand Down Expand Up @@ -57,6 +58,8 @@ app.use(express.static(path.resolve(__dirname, '../dist'), { maxage }));
app.use('/public', express.static(config.publicAssets));
app.use(/\/((?!remotepublic).)*/, bodyParser.json({ limit: '1mb' }));

app.use(appContextMiddleware);

//////
// this middleware should go just before any other that accesses to db
app.use(multitenantMiddleware);
Expand Down
12 changes: 11 additions & 1 deletion database/reindex_elastic.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,17 @@ process.on('unhandledRejection', error => {
throw error;
});

DB.connect().then(async () => {
let dbAuth = {};

if (process.env.DBUSER) {
dbAuth = {
auth: { authSource: 'admin' },
user: process.env.DBUSER,
pass: process.env.DBPASS,
};
}

DB.connect(config.DBHOST, dbAuth).then(async () => {
const start = Date.now();

await tenants.run(async () => {
Expand Down
17 changes: 17 additions & 0 deletions e2e/helpers/disableTransitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*global page*/

export default async () => {
await page.addStyleTag({
content: `
*,
*::after,
*::before {
transition-delay: 0s !important;
transition-duration: 0s !important;
animation-delay: -0.0001s !important;
animation-duration: 0s !important;
animation-play-state: paused !important;
caret-color: transparent !important;
}`,
});
};
Loading

0 comments on commit 4407b56

Please sign in to comment.