Skip to content

Commit

Permalink
About page mongodb version (#172)
Browse files Browse the repository at this point in the history
* mongodb version added to appLayer, repos

* bit of refactor to make system info elements follow conventions

* mongodbversion display on about page, add enviromentInfo

* systemInfo tests, authApiAppender moved into /api endpoint

* PR fixes

* mongodbversion typo

* typo

* config.js versions

* [service] add semantic version components to system info and test stub

* WIP - tests,app.systemInfo,webController

* version assertion change, string to obj

* tests for app.impl.systemInfo, and adapters.systemInfo webcontroller

* WIP - skip test append

* DI refactor of CreateReadSystemInfo,test passing

* remove .skip

* two URL obj icon db test

* system info tests, revert back icon test urls

---------

Co-authored-by: Robert St. John <[email protected]>
  • Loading branch information
kimura-developer and restjohn authored Aug 28, 2023
1 parent 5b7aadb commit 0fcdb25
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 421 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

import express from 'express'
import { WebAppRequestFactory } from '../adapters.controllers.web'
import { SystemInfoAppLayer } from '../../app.api/systemInfo/app.api.systemInfo'


export function SystemInfoRoutes(appLayer: SystemInfoAppLayer, createAppRequest: WebAppRequestFactory): express.Router {

const routes = express.Router()

routes.route('/')
.get(async (req, res, next) => {
const appReq = createAppRequest(req)

const appRes = await appLayer.readSystemInfo(appReq)
if (appRes.success) {
return res.json(appRes.success)
}
return next(appRes.error)
})

return routes
}
25 changes: 25 additions & 0 deletions service/src/adapters/systemInfo/adapters.systemInfo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Connection } from 'mongoose'
import { EnvironmentService, EnvironmentInfo } from '../../entities/systemInfo/entities.systemInfo'

export class EnvironmentServiceImpl implements EnvironmentService {

private mongodbVersion: string | null = null
private readonly nodeVersion: string = process.versions.node

constructor(private readonly dbConn: Connection) {}

async readEnvironmentInfo(): Promise<EnvironmentInfo> {
if (this.mongodbVersion === null) {
const dbInfo = await this.dbConn.db.admin().serverInfo()
this.mongodbVersion = dbInfo.version
}
return {
nodeVersion: this.nodeVersion,
monogdbVersion: this.mongodbVersion!,
}
}

readDependencies(): Promise<unknown> {
throw new Error('Method not implemented.')
}
}
19 changes: 19 additions & 0 deletions service/src/app.api/systemInfo/app.api.systemInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SystemInfo } from '../../entities/systemInfo/entities.systemInfo'
import { InfrastructureError } from '../app.api.errors'
import { AppRequest, AppResponse } from '../app.api.global'


export type ExoPrivilegedSystemInfo = SystemInfo
export type ExoRedactedSystemInfo = Omit<SystemInfo, 'environment'>
export type ExoSystemInfo = ExoPrivilegedSystemInfo | ExoRedactedSystemInfo

export interface ReadSystemInfoRequest extends AppRequest {}
export interface ReadSystemInfoResponse extends AppResponse<ExoSystemInfo, InfrastructureError> {}

export interface ReadSystemInfo {
(req: ReadSystemInfoRequest): Promise<ReadSystemInfoResponse>
}

export interface SystemInfoAppLayer {
readSystemInfo: ReadSystemInfo
}
66 changes: 66 additions & 0 deletions service/src/app.impl/systemInfo/app.impl.systemInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { AppResponse } from '../../app.api/app.api.global';
import * as api from '../../app.api/systemInfo/app.api.systemInfo';
import { EnvironmentService } from '../../entities/systemInfo/entities.systemInfo';
import * as Settings from '../../models/setting';
import * as AuthenticationConfiguration from '../../models/authenticationconfiguration';
import AuthenticationConfigurationTransformer from '../../transformers/authenticationconfiguration';
/**
* This factory function creates the implementation of the {@link api.ReadSystemInfo}
* application layer interface.
*/
export function CreateReadSystemInfo(
environmentService: EnvironmentService,
config: any,
settingsModule: typeof Settings = Settings,
authConfigModule: typeof AuthenticationConfiguration = AuthenticationConfiguration,
authConfigTransformerModule: typeof AuthenticationConfigurationTransformer = AuthenticationConfigurationTransformer
): api.ReadSystemInfo {

// appending the authentication strategies to the api
async function appendAuthenticationStrategies(
api: any,
options: any = {}
): Promise<any> {
const apiCopy = {
...api,
authenticationStrategies: {}
};
const authenticationConfigurations = await authConfigModule.getAllConfigurations();
const transformedConfigurations = authConfigTransformerModule.transform(
authenticationConfigurations.filter(
config => config.enabled || options.includeDisabled
),
options
);
transformedConfigurations.forEach(
(configuration: { name: string | number }) => {
apiCopy.authenticationStrategies[configuration.name] = {
...configuration
};
}
);
return apiCopy;
}

return async function readSystemInfo(
req: api.ReadSystemInfoRequest
): Promise<api.ReadSystemInfoResponse> {
// TODO: will need a permission check to determine what level of system
// information the requesting principal is allowed to see
const environment = await environmentService.readEnvironmentInfo();
const disclaimer = (await settingsModule.getSetting('disclaimer')) || {};
const contactInfo = (await settingsModule.getSetting('contactInfo')) || {};

const apiConfig = Object.assign({}, config.api, {
environment: environment,
disclaimer: disclaimer,
contactInfo: contactInfo
});

const updatedApiConfig = await appendAuthenticationStrategies(apiConfig, {
whitelist: true
});

return AppResponse.success(updatedApiConfig as any);
};
}
38 changes: 34 additions & 4 deletions service/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fs from 'fs-extra'
import mongoose from 'mongoose'
import express from 'express'
import util from 'util'
import apiConfig from './config';
import { MongooseFeedServiceTypeRepository, FeedServiceTypeIdentityModel, MongooseFeedServiceRepository, FeedServiceModel, MongooseFeedRepository, FeedModel } from './adapters/feeds/adapters.feeds.db.mongoose'
import { waitForDefaultMongooseConnection } from './adapters/adapters.db.mongoose'
import { FeedServiceTypeRepository, FeedServiceRepository, FeedRepository } from './entities/feeds/entities.feeds'
Expand Down Expand Up @@ -40,6 +41,7 @@ import { UserRepositoryToken } from './plugins.api/plugins.api.users'
import { StaticIconRepositoryToken } from './plugins.api/plugins.api.icons'
import { UserModel, MongooseUserRepository } from './adapters/users/adapters.users.db.mongoose'
import { UserRepository, UserExpanded } from './entities/users/entities.users'
import { EnvironmentService } from './entities/systemInfo/entities.systemInfo'
import { WebRoutesHooks, GetAppRequestContext } from './plugins.api/plugins.api.web'
import { UsersAppLayer, UsersRoutes } from './adapters/users/adapters.users.controllers.web'
import { SearchUsers } from './app.impl/users/app.impl.users'
Expand All @@ -56,6 +58,13 @@ import { FileSystemAttachmentStoreInitError, intializeAttachmentStore } from './
import { AttachmentStoreToken, ObservationRepositoryToken } from './plugins.api/plugins.api.observations'
import { GetDbConnection, MongooseDbConnectionToken } from './plugins.api/plugins.api.db'
import { EventEmitter } from 'events'
import { EnvironmentServiceImpl } from './adapters/systemInfo/adapters.systemInfo.service'
import { SystemInfoAppLayer } from './app.api/systemInfo/app.api.systemInfo'
import { CreateReadSystemInfo } from './app.impl/systemInfo/app.impl.systemInfo'
import Settings from "./models/setting";
import AuthenticationConfiguration from "./models/authenticationconfiguration";
import AuthenticationConfigurationTransformer from "./transformers/authenticationconfiguration";
import { SystemInfoRoutes } from './adapters/systemInfo/adapters.systemInfo.controllers.web'


export interface MageService {
Expand Down Expand Up @@ -124,7 +133,6 @@ export const boot = async function(config: BootConfig): Promise<MageService> {
const dbLayer = await initDatabase()
const repos = await initRepositories(dbLayer, config)
const appLayer = await initAppLayer(repos)

const { webController, addAuthenticatedPluginRoutes } = await initWebLayer(repos, appLayer, config.plugins?.webUIPlugins || [])
const routesForPluginId: { [pluginId: string]: WebRoutesHooks['webRoutes'] } = {}
const collectPluginRoutesToSort = (pluginId: string, initPluginRoutes: WebRoutesHooks['webRoutes']) => {
Expand Down Expand Up @@ -252,7 +260,8 @@ type AppLayer = {
deleteFeed: feedsApi.DeleteFeed
},
icons: StaticIconsAppLayer,
users: UsersAppLayer
users: UsersAppLayer,
systemInfo: SystemInfoAppLayer
}

async function initDatabase(): Promise<DatabaseLayer> {
Expand Down Expand Up @@ -318,7 +327,8 @@ type Repositories = {
},
users: {
userRepo: UserRepository
}
},
enviromentInfo: EnvironmentService
}

// TODO: the real thing
Expand Down Expand Up @@ -347,6 +357,7 @@ async function initRepositories(models: DatabaseLayer, config: BootConfig): Prom
[ new PluginUrlScheme(config.plugins?.servicePlugins || []) ])
const userRepo = new MongooseUserRepository(models.users.user)
const attachmentStore = await intializeAttachmentStore(environment.attachmentBaseDirectory)
const systemInfoService = new EnvironmentServiceImpl(models.conn)
if (attachmentStore instanceof FileSystemAttachmentStoreInitError) {
throw attachmentStore
}
Expand All @@ -366,7 +377,8 @@ async function initRepositories(models: DatabaseLayer, config: BootConfig): Prom
},
users: {
userRepo
}
},
enviromentInfo: systemInfoService
}
}

Expand All @@ -376,12 +388,14 @@ async function initAppLayer(repos: Repositories): Promise<AppLayer> {
const icons = await initIconsAppLayer(repos)
const feeds = await initFeedsAppLayer(repos)
const users = await initUsersAppLayer(repos)
const systemInfo = initSystemInfoAppLayer(repos)
return {
events,
observations,
feeds,
icons,
users,
systemInfo
}
}

Expand Down Expand Up @@ -463,6 +477,18 @@ function initFeedsAppLayer(repos: Repositories): AppLayer['feeds'] {
}
}

function initSystemInfoAppLayer(repos: Repositories): SystemInfoAppLayer {
return {
readSystemInfo: CreateReadSystemInfo(
repos.enviromentInfo,
apiConfig,
Settings,
AuthenticationConfiguration,
AuthenticationConfigurationTransformer
)
}
}

interface MageEventRequestContext extends AppRequestContext<UserDocument> {
event: MageEventDocument | MageEvent | undefined
}
Expand Down Expand Up @@ -503,6 +529,10 @@ async function initWebLayer(repos: Repositories, app: AppLayer, webUIPlugins: st
bearerAuth,
iconsRoutes
])
const systemInfoRoutes = SystemInfoRoutes(app.systemInfo, appRequestFactory)
webController.use('/api', [
systemInfoRoutes
])
const observationRequestFactory: ObservationWebAppRequestFactory = <Params extends object | undefined>(req: express.Request, params: Params) => {
const context: observationsApi.ObservationRequestContext = {
...baseAppRequestContext(req),
Expand Down
4 changes: 2 additions & 2 deletions service/src/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const packageJson = require('../package');
const version = packageJson.version.split('.');

const version = packageJson.version.split(".");

module.exports = {
api: {
Expand All @@ -11,7 +11,7 @@ module.exports = {
major: parseInt(version[0]),
minor: parseInt(version[1]),
micro: parseInt(version[2])
}
},
},
server: {
locationServices: {
Expand Down
28 changes: 28 additions & 0 deletions service/src/entities/systemInfo/entities.systemInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

export interface EnvironmentInfo {
nodeVersion: string
monogdbVersion: string
// TODO: maybe relavant environment variables? redact sensitive values
}

export interface EnvironmentService {
readEnvironmentInfo(): Promise<EnvironmentInfo>
// TODO: how to build dependency list/tree with all versions
readDependencies(): Promise<unknown>
}

export interface SystemInfo {
/**
* These [semantic](https://semver.org/) version components are parsed from
* the package version to allow the mobile apps to check compatibility with
* the server. Without this structure, the apps will not allow interaction
* with the server.
*/
version: { major: number, minor: number, micro: number }
/**
* Package version string straight from package.json
*/
environment: EnvironmentInfo
disclaimer: any // mongoose Document type
contactInfo: any
}
Loading

0 comments on commit 0fcdb25

Please sign in to comment.