Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add realm limit settings (#151) #152

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
This changelog documents all notable changes of the RealMQ Platform.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added
- Introduce limit settings on realm level ([#151]).

[#151]: https://github.com/realmq/realmq-platform/issues/151

## [0.3.0] - 2023-07-06

### Added
Expand Down
2 changes: 2 additions & 0 deletions src/api/admin/v1/mappers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const authList = require('./auth-list');
const channel = require('./channel');
const channelList = require('./channel-list');
const realm = require('./realm');
const realmDetails = require('./realm-details');
const realmList = require('./realm-list');
const subscription = require('./subscription');
const subscriptionList = require('./subscription-list');
Expand All @@ -18,6 +19,7 @@ module.exports = {
channel,
channelList,
realm,
realmDetails,
realmList,
subscription,
subscriptionList,
Expand Down
30 changes: 30 additions & 0 deletions src/api/admin/v1/mappers/realm-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const mapGenericEntity = require('../../../../lib/mappers/generic-entity');
const mapRealm = require('./realm');

/**
* @class RealmViewModel
* @property {string} id
* @property {string} name
* @property {Date} createdAt
* @property {Date} updatedAt
*/
/**
* Map a realm to detail response view model
* @param {object} args
* @param {RealmModel} args.realm The realm entity
* @param {RealmLimitsModel} args.realmLimits The realm limits entity
* @return {RealmViewModel} The realm view model
*/
module.exports = ({realm, realmLimits}) => ({
...mapRealm(realm),
limits: mapGenericEntity({
entity: realmLimits,
propertyMap: {
maxConnections: 'maxConnections',
sessionMaxMessageRate: 'sessionMaxMessageRate',
sessionMaxConnectionLifetime: 'sessionMaxConnectionLifetime',
sessionMaxMessageSize: 'sessionMaxMessageSize',
},
}),
});

2 changes: 1 addition & 1 deletion src/api/admin/v1/mappers/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const mapGenericEntity = require('../../../../lib/mappers/generic-entity');
* @property {Date} updatedAt
*/
/**
* Map an realm to response view model
* Map a realm to response view model
* @param {RealmModel} realm The realm entity
* @return {RealmViewModel} The realm view model
*/
Expand Down
28 changes: 27 additions & 1 deletion src/api/admin/v1/openapi/definitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,39 @@ TimestampsTrait:
Realm:
allOf:
- $ref: '#/definitions/BaseEntity'
- properties:
- type: object
properties:
name:
type: string
required:
- name
- $ref: '#/definitions/TimestampsTrait'

RealmDetails:
allOf:
- $ref: '#/definitions/Realm'
- type: object
properties:
limits:
$ref: '#/definitions/RealmLimits'

RealmLimits:
type: object
properties:
maxConnections:
description: Maximum number of simultaneously online connections
type: integer
sessionMaxMessageRate:
description: Maximum incoming publish rate per session per second
type: integer
sessionMaxConnectionLifetime:
description: Maximum lifetime of a connection in seconds
type: integer
sessionMaxMessageSize:
description: Maximum size of a message payload in bytes
type: integer
additionalProperties: false

RealmList:
allOf:
- $ref: '#/definitions/BaseList'
Expand Down
9 changes: 6 additions & 3 deletions src/api/admin/v1/routes/realms.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ module.exports = (tasks, mappers) => ({
},

async post(request, response) {
const {body: {name}} = request;
const {ok, result: realm, error} = await tasks.admin.createRealm({name});
const {body: {name, limits = {}}} = request;
const {ok, result, error} = await tasks.admin.createRealm({name, limits});

if (!ok) {
throw error;
}

return response.status(201).json(mappers.realm(realm));
return response.status(201).json(mappers.realmDetails({
realm: result.realm,
realmLimits: result.realmLimits,
}));
},
});
4 changes: 3 additions & 1 deletion src/api/admin/v1/routes/realms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
name:
description: The name of the real, eg. project/org name
type: string
limits:
$ref: '#/definitions/RealmLimits'
responses:
201:
description: Realm was successfully created.
schema:
$ref: '#/definitions/Realm'
$ref: '#/definitions/RealmDetails'
400:
description: Request validation failed.
401:
Expand Down
9 changes: 6 additions & 3 deletions src/api/admin/v1/routes/realms/{id}.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
module.exports = (tasks, mappers) => ({
async get(request, response) {
const {params: {id}} = request;
const {ok, result: realm, error} = await tasks.admin.fetchRealm({id});
const {ok, result, error} = await tasks.admin.fetchRealm({id});
if (!ok) {
throw error;
}

if (!realm) {
if (!result.realm) {
return response.status(404).json({
code: 'UnknownRealm',
message: 'Cannot lookup the given realm.',
});
}

response.json(mappers.realm(realm));
response.json(mappers.realmDetails({
realm: result.realm,
realmLimits: result.realmLimits,
}));
},
});
2 changes: 1 addition & 1 deletion src/api/admin/v1/routes/realms/{id}.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
200:
description: The realm was loaded sucessfully.
schema:
$ref: '#/definitions/Realm'
$ref: '#/definitions/RealmDetails'
400:
description: Request validation failed.
401:
Expand Down
20 changes: 17 additions & 3 deletions src/api/broker/v1/routes/vmq/auth-on-register.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const stripUndefined = require('../../../../../lib/strip-undefined');

/**
*
* @param {BrokerTasks#authorizeRegister} authorizeRegister Task
Expand All @@ -16,7 +18,19 @@ module.exports = ({authorizeRegister}) =>
return response.status(400).send();
}

const authorized = await authorizeRegister(clientId);
const result = authorized ? 'ok' : 'next';
response.json({result});
const {authorized, realmLimits} = await authorizeRegister(clientId);
if (!authorized) {
return response.json({result: 'next'});
}

const modifiers = stripUndefined({
// eslint-disable-next-line camelcase
max_message_size: realmLimits?.sessionMaxMessageSize,
});

response.json({
result: 'ok',
// VerneMQ fails if modifiers is an empty object
modifiers: Object.entries(modifiers).length > 0 ? modifiers : undefined,
});
};
2 changes: 1 addition & 1 deletion src/api/broker/v1/routes/vmq/auth-on-register.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('An auth-on-register handler', () => {
beforeEach(() => {
authOnRegister = initAuthOnRegister({
async authorizeRegister(clientId) {
return clientId === authorizedClientId;
return {authorized: clientId === authorizedClientId};
},
});
responseState = {};
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/repositories.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = async ({db}) => {
channelCollection,
messageCollection,
realmCollection,
realmLimitsCollection,
realtimeConnectionCollection,
subscriptionCollection,
userCollection,
Expand All @@ -20,6 +21,7 @@ module.exports = async ({db}) => {
db.collection('channels'),
db.collection('messages'),
db.collection('realms'),
db.collection('realm-limits'),
db.collection('realtime-connections'),
db.collection('subscriptions'),
db.collection('users'),
Expand All @@ -29,6 +31,7 @@ module.exports = async ({db}) => {
channelCollection,
messageCollection,
realmCollection,
realmLimitsCollection,
realtimeConnectionCollection,
subscriptionCollection,
userCollection,
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = ({
channel: channelRepository,
message: messageRepository,
realm: realmRepository,
realmLimits: realmLimitsRepository,
realtimeConnection: realtimeConnectionRepository,
subscription: subscriptionRepository,
user: userRepository,
Expand All @@ -45,6 +46,7 @@ module.exports = ({
channelRepository,
messageRepository,
realmRepository,
realmLimitsRepository,
subscriptionRepository,
userRepository,
sendSubscriptionSyncMessage,
Expand All @@ -55,6 +57,7 @@ module.exports = ({
authRepository,
channelRepository,
subscriptionRepository,
realmLimitsRepository,
realtimeConnectionRepository,
messageRepository,
userRepository,
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ const createLogger = require('./lib/logger');
process.exit(1);
}
})();

36 changes: 36 additions & 0 deletions src/models/realm-limits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const stripUndefined = require('../lib/strip-undefined');

/**
* @typedef {Object} RealmLimitsModel Specialized set of realm settings focused on connections/sessions
* @property {string} [id]
* @property {string} realmId
* @property {number} maxConnections Max number of simultaneously online connections
* @property {number} sessionMaxMessageRate Max incoming publish rate per session per second
* @property {number} sessionMaxConnectionLifetime Max lifetime of a connection in seconds
* @property {number} sessionMaxMessageSize Max message payload size in bytes
* @property {Date} [createdAt]
* @property {Date} [updatedAt]
*/

/**
* @return {RealmLimitsModel} The generalized realm model
*/
module.exports = ({
id,
realmId,
maxConnections,
sessionMaxMessageRate,
sessionMaxConnectionLifetime,
sessionMaxMessageSize,
createdAt,
updatedAt,
}) => stripUndefined({
id,
realmId,
maxConnections,
sessionMaxMessageRate,
sessionMaxConnectionLifetime,
sessionMaxMessageSize,
createdAt,
updatedAt,
});
13 changes: 11 additions & 2 deletions src/repositories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const createAuthRepository = require('./auth');
const createChannelRepository = require('./channel');
const createMessageRepository = require('./message');
const createRealmRepository = require('./realm');
const createRealmLimitsRepository = require('./realm-limits');
const createRealtimeConnectionRepository = require('./realtime-connection');
const createSubscriptionRepository = require('./subscription');
const createUserRepository = require('./user');
Expand All @@ -13,14 +14,20 @@ const createUserRepository = require('./user');
* @param {Collection} channelCollection Dependency
* @param {Collection} messageCollection Dependency
* @param {Collection} realmCollection Dependency
* @param {Collection} realmLimitsCollection Dependency
* @param {Collection} realtimeConnectionCollection Dependency
* @param {Collection} subscriptionCollection Dependency
* @param {Collection} userCollection Dependency
* @return {Repositories} The repositories
*/
module.exports = ({
authCollection, channelCollection, messageCollection,
realmCollection, subscriptionCollection, userCollection,
authCollection,
channelCollection,
messageCollection,
realmCollection,
realmLimitsCollection,
subscriptionCollection,
userCollection,
realtimeConnectionCollection,
}) =>
/**
Expand All @@ -29,6 +36,7 @@ module.exports = ({
* @property {ChannelRepository} channel
* @property {MessageRepository} message
* @property {RealmRepository} realm
* @property {RealmLimitsRepository} realmLimits
* @property {RealtimeConnectionRepository} realtimeConnection
* @property {SubscriptionRepository} subscription
* @property {UserRepository} user
Expand All @@ -38,6 +46,7 @@ module.exports = ({
channel: createChannelRepository({collection: channelCollection}),
message: createMessageRepository({collection: messageCollection}),
realm: createRealmRepository({collection: realmCollection}),
realmLimits: createRealmLimitsRepository({collection: realmLimitsCollection}),
realtimeConnection: createRealtimeConnectionRepository({collection: realtimeConnectionCollection}),
subscription: createSubscriptionRepository({collection: subscriptionCollection}),
user: createUserRepository({collection: userCollection}),
Expand Down
31 changes: 31 additions & 0 deletions src/repositories/realm-limits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const createRealmLimitsModel = require('../models/realm-limits');
const createMongoRepository = require('./lib/mongo');

/**
* Create realm repository.
*
* @param {Collection} collection Mongodb collection
* @param {function} createModel Model factory
* @return {RealmLimitsRepository} The created realm repository
*/
module.exports = ({collection, createModel = createRealmLimitsModel}) => {
const mongoRepo = createMongoRepository({collection, createModel});

/**
* @class RealmLimitsRepository
* @extends MongoRepository
*/
return {
...mongoRepo,

/**
*
* @param {string} realmId
* @return {Promise<RealmLimitsModel|null>}
*/
async findOneByRealmId(realmId) {
return mongoRepo.findOne({realmId});
},
};
};

Loading