Skip to content

Commit

Permalink
version 1.43.0
Browse files Browse the repository at this point in the history
  • Loading branch information
daneryl committed Nov 22, 2021
2 parents 8d25a17 + 0754ea8 commit 341e6f5
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 20 deletions.
17 changes: 17 additions & 0 deletions app/api/tenants/specs/tenantsContext.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { config } from 'api/config';
import { tenants } from '../tenantContext';

describe('tenantsContext', () => {
describe('add', () => {
it('should add defaults to tenant added', async () => {
tenants.add({ name: 'test-tenant', dbName: 'test-tenant-db' });
await tenants.run(async () => {
expect(tenants.current()).toMatchObject({
...config.defaultTenant,
name: 'test-tenant',
dbName: 'test-tenant-db',
});
}, 'test-tenant');
});
});
});
30 changes: 28 additions & 2 deletions app/api/tenants/specs/tenantsModel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Model } from 'mongoose';
import waitForExpect from 'wait-for-expect';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import testingDB from 'api/utils/testing_db';
import { TenantsModel } from '../tenantsModel';
import { TenantsModel, tenantsModel } from '../tenantsModel';

describe('tenantsModel', () => {
let db: Db;
Expand Down Expand Up @@ -35,7 +35,7 @@ describe('tenantsModel', () => {
};

spyOn(Model, 'watch').and.returnValue(mockChangeStream);
model = new TenantsModel();
model = await tenantsModel();

await db.collection('tenants').deleteMany({});
await db.collection('tenants').insertMany([
Expand Down Expand Up @@ -94,6 +94,32 @@ describe('tenantsModel', () => {
});
});

it('should require name', async () => {
try {
await db.collection('tenants').insertOne({ name: '' });
await db.collection('tenants').insertOne({});
fail('should fail with required error');
} catch (e) {
const validationFailed = 121;
expect(e.code).toBe(validationFailed);
}
});

it('should requiere a unique name for tenants', async () => {
try {
await db.collection('tenants').insertMany([
{
name: 'tenant one',
},
]);
} catch (e) {
const duplicateKeyError = 11000;
expect(e.code).toBe(duplicateKeyError);
}
const tenants = await model.get();
expect(tenants).toMatchObject([{ name: 'tenant one' }, { name: 'tenant two' }]);
});

describe('on error', () => {
it('watch not supported should close the connection', () => {
errorEvent({
Expand Down
22 changes: 12 additions & 10 deletions app/api/tenants/tenantContext.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { config } from 'api/config';
import { handleError } from 'api/utils';
import { appContext } from 'api/utils/AppContext';
import { TenantsModel } from './tenantsModel';
import { TenantDocument, TenantsModel, DBTenant, tenantsModel } from './tenantsModel';

export type Tenant = {
name: string;
Expand All @@ -17,14 +17,17 @@ export type Tenant = {
class Tenants {
tenants: { [k: string]: Tenant };

constructor() {
defaultTenant: Tenant;

constructor(defaultTenant: Tenant) {
this.defaultTenant = defaultTenant;
this.tenants = {
[config.defaultTenant.name]: config.defaultTenant,
[config.defaultTenant.name]: defaultTenant,
};
}

async setupTenants() {
const model = new TenantsModel();
const model = await tenantsModel();
model.on('change', () => {
this.updateTenants(model).catch(handleError);
});
Expand All @@ -34,8 +37,8 @@ class Tenants {
async updateTenants(model: TenantsModel) {
const tenants = await model.get();

tenants.forEach((tenant: Tenant) => {
this.add(tenant);
tenants.forEach((tenant: TenantDocument) => {
this.add(tenant.toObject());
});
}

Expand Down Expand Up @@ -69,11 +72,10 @@ class Tenants {
return this.tenants[tenantName];
}

add(tenant: Tenant) {
this.tenants[tenant.name] = tenant;
add(tenant: DBTenant) {
this.tenants[tenant.name] = { ...this.defaultTenant, ...tenant };
}
}

const tenants = new Tenants();

const tenants = new Tenants(config.defaultTenant);
export { tenants };
62 changes: 57 additions & 5 deletions app/api/tenants/tenantsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@ import { MongoError } from 'mongodb';

import { Tenant } from './tenantContext';

const schemaValidator = {
$jsonSchema: {
bsonType: 'object',
properties: {
name: {
bsonType: 'string',
description: 'must be a string and is required',
minLength: 1,
},
},
},
};

const mongoSchema = new mongoose.Schema({
name: String,
name: { type: String, unique: true },
dbName: String,
indexName: String,
uploadedDocuments: String,
Expand All @@ -18,15 +31,24 @@ const mongoSchema = new mongoose.Schema({
activityLogs: String,
});

type TenantDocument = Document & Tenant;
export type DBTenant = Partial<Tenant> & { name: string };
export type TenantDocument = Document & DBTenant;

export class TenantsModel extends EventEmitter {
model: Model<TenantDocument>;
model?: Model<TenantDocument>;

tenantsDB: mongoose.Connection;

collectionName: string;

constructor() {
super();
const tenantsDB = DB.connectionForDB(config.SHARED_DB);
this.model = tenantsDB.model<TenantDocument>('tenants', mongoSchema);
this.collectionName = 'tenants';
this.tenantsDB = DB.connectionForDB(config.SHARED_DB);
}

private initializeModel() {
this.model = this.tenantsDB.model<TenantDocument>(this.collectionName, mongoSchema);

const changeStream = this.model.watch();
changeStream.on('change', () => {
Expand All @@ -47,12 +69,42 @@ export class TenantsModel extends EventEmitter {
});
}

async initialize() {
const collections = (await this.tenantsDB.db.listCollections().toArray()).map(c => c.name);

if (collections.includes(this.collectionName)) {
await this.tenantsDB.db.command({
collMod: this.collectionName,
validator: schemaValidator,
});
} else {
await this.tenantsDB.db.createCollection(this.collectionName, {
validator: schemaValidator,
});
}

this.initializeModel();
}

async change() {
const tenants = await this.get();
this.emit('change', tenants);
}

async get() {
if (!this.model) {
throw new Error(
'tenants model has not been initialized, make sure you called initialize() method'
);
}
return this.model.find({});
}
}

const tenantsModel = async () => {
const model = new TenantsModel();
await model.initialize();
return model;
};

export { tenantsModel };
3 changes: 1 addition & 2 deletions app/api/utils/specs/multitenantMiddleware.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const testingRoutes = (app: Application) => {

describe('multitenant middleware', () => {
it('should execute next middlewares inside a tenant async context', async () => {
//@ts-ignore
tenants.add({ name: 'test' });

const app: Application = express();
Expand All @@ -25,6 +24,6 @@ describe('multitenant middleware', () => {
.get('/api/testGET')
.set('tenant', 'test');

expect(response.text).toBe(JSON.stringify({ name: 'test' }));
expect(JSON.parse(response.text)).toMatchObject({ name: 'test' });
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "uwazi",
"version": "1.43.0-rc1",
"version": "1.43.0",
"description": "Uwazi is a free, open-source solution for organising, analysing and publishing your documents.",
"keywords": [
"react"
Expand Down

0 comments on commit 341e6f5

Please sign in to comment.