Skip to content

Commit

Permalink
Get config.json from collaboration server (#371)
Browse files Browse the repository at this point in the history
* Add a JBrowse module

* Tracks added - ready for test

* tmp commit

* tmp commit

* Get Apollo tracks from Mongo and include those in config.json

* tmp commit

* user can add track to Mongo from track info menu

* user can add/delete track to Mongo from track info menu

* Ready for draft PR

* Jbrowse -> JBrowse

* Use a 'none' role instead of public endpoints

* Add "Log out" Apollo menu item

* Fetch authenticated config.json from collab server

* Misc. fixes

* Store whole JBrowse config instead of just tracks

* Save session tracks to Apollo JBrowse config

* Fix feature selection refresh crash

* Remove static serving from collaboration server

* Fix/skip a few more tests

* Remove unused variable

* Fix CLI test env

* Move env variables to the correct place

---------

Co-authored-by: Garrett Stevens <[email protected]>
  • Loading branch information
kyostiebi and garrettjstevens authored Aug 1, 2024
1 parent cb1aced commit 6fbe4ac
Show file tree
Hide file tree
Showing 42 changed files with 838 additions and 239 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Start apollo
run: |
yarn --cwd packages/apollo-shared start &
yarn --cwd packages/apollo-collaboration-server start &
ALLOW_ROOT_USER=true ROOT_USER_NAME=admin ROOT_USER_PASSWORD=pass yarn --cwd packages/apollo-collaboration-server start &
- name: Run CLI tests
run: python3 ./test/test.py TestCLI
working-directory: packages/apollo-cli
Expand Down
6 changes: 0 additions & 6 deletions docs/apollo.env
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ ALLOW_GUEST_USER=true
# Possible values are admin, readOnly and user; defaults to readonly
GUEST_USER_ROLE=readOnly

# Apollo by default uses The Sequence Ontology. You can override this by
# providing a path to an ontology file in the OBO Graphs JSON format. You can
# use `robot` to convert an OBO or OWL to OBO Graphs JSON.
# http://robot.obolibrary.org/convert
# ONTOLOGY_FILE = '/data/ontology.json'

# Comma-separated list of Apollo plugins to use
# PLUGIN_URLS=https://example.com/apollo-plugin-example.umd.production.min.js
# Alternatively, can be a path to a file with a list of plugin URLs, one URL per
Expand Down
35 changes: 21 additions & 14 deletions packages/apollo-collaboration-server/.development.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# URL
URL=http://localhost:3999

# Name of your server (shown during the login process)
NAME=Demo Server

# MongoDB connection
MONGODB_URI=mongodb://127.0.0.1:27017/apolloDb
# Alternatively, can be a path to a file with the URI
Expand Down Expand Up @@ -48,6 +51,13 @@ MICROSOFT_CLIENT_SECRET=~Gr8Q~h6RTU7SMC-fjNxXy_~nabTD-ME_rFyLa.M
## OPTIONAL ##
##############

# Description of what is hosted on your server
DESCRIPTION=A server for Apollo development purposes

# URL (relative or absolute) of the Apollo JBrowse plugin.
# Defaults to relative URL 'apollo.js'
PLUGIN_LOCATION=http://localhost:9000/dist/jbrowse-plugin-apollo.umd.development.js

# Application port, defaults to 3999
# PORT=3999

Expand All @@ -69,28 +79,25 @@ CHUNK_SIZE=500
# Whether to broadcast users locations, defaults to true
# BROADCAST_USER_LOCATION=true

# Whether to allow a root user that can log in with a name and password. All
# other users (besides guest) must sign in with an authentication provider.
# Defaults to false
# ALLOW_ROOT_USER=false
# The root user name, required if ALLOW_ROOT_USER is true
# ROOT_USER_NAME=root
# The root user password, required if ALLOW_ROOT_USER is true
# ROOT_USER_PASSWORD=password
# Alternatively, can be a path to a file with the root user password
# ROOT_USER_PASSWORD_FILE=/run/secrets/root-user-password

# Whether to allow guest users who do not have to log in, defaults to false
ALLOW_GUEST_USER=true
# If guest users are allowed, what role will they have
# Possible values are admin, readOnly and user; defaults to readonly
# GUEST_USER_ROLE=readOnly

# Apollo by default uses The Sequence Ontology. You can override this by
# providing a path to an ontology file in the OBO Graphs JSON format. You can
# use `robot` to convert an OBO or OWL to OBO Graphs JSON.
# http://robot.obolibrary.org/convert
# ONTOLOGY_FILE = '/data/ontology.json'

# Comma-separated list of Apollo plugins to use
# PLUGIN_URLS=https://example.com/apollo-plugin-example.umd.production.min.js
# Alternatively, can be a path to a file with a list of plugin URLs, one URL per
# line
# PLUGIN_URLS_FILE=/data/plugin-urls

##############################################################################
## To login via username and password
##############################################################################
ALLOW_ROOT_USER=false
ROOT_USER_NAME=admin
ROOT_USER_PASSWORD=pass
ROOT_USER_PASSWORD_FILE
1 change: 1 addition & 0 deletions packages/apollo-collaboration-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@nestjs/terminus": "^10.0.1",
"@nestjs/websockets": "^10.1.0",
"connect-mongodb-session": "^3.1.1",
"deepmerge": "^4.3.1",
"express": "^4.18.0",
"express-session": "^1.17.3",
"generic-filehandle": "^3.0.0",
Expand Down
11 changes: 5 additions & 6 deletions packages/apollo-collaboration-server/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import fs from 'node:fs/promises'
import path from 'node:path'

import { Module } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { APP_GUARD } from '@nestjs/core'
import { MongooseModule, MongooseModuleFactoryOptions } from '@nestjs/mongoose'
import { ServeStaticModule } from '@nestjs/serve-static'
import Joi from 'joi'
import { Connection } from 'mongoose'

Expand All @@ -18,6 +16,7 @@ import { ExportModule } from './export/export.module'
import { FeaturesModule } from './features/features.module'
import { FilesModule } from './files/files.module'
import { HealthModule } from './health/health.module'
import { JBrowseModule } from './jbrowse/jbrowse.module'
import { MessagesModule } from './messages/messages.module'
import { OperationsModule } from './operations/operations.module'
import { PluginsModule } from './plugins/plugins.module'
Expand All @@ -38,6 +37,7 @@ const nodeEnv = process.env.NODE_ENV ?? 'production'
const validationSchema = Joi.object({
// Required
URL: Joi.string().uri().required(),
NAME: Joi.string().required(),
MONGODB_URI: Joi.string(),
MONGODB_URI_FILE: Joi.string(),
FILE_UPLOAD_FOLDER: Joi.string().required(),
Expand All @@ -53,8 +53,9 @@ const validationSchema = Joi.object({
JWT_SECRET_FILE: Joi.string(),
SESSION_SECRET: Joi.string(),
SESSION_SECRET_FILE: Joi.string(),
ONTOLOGY_FILE: Joi.string(),
// Optional
DESCRIPTION: Joi.string(),
PLUGIN_LOCATION: Joi.string(),
ALLOW_ROOT_USER: Joi.boolean().default(false),
ROOT_USER_NAME: Joi.string(),
ROOT_USER_PASSWORD: Joi.string(),
Expand Down Expand Up @@ -164,10 +165,8 @@ async function mongoDBURIFactory(
RefSeqChunksModule,
RefSeqsModule,
SequenceModule,
ServeStaticModule.forRoot({
rootPath: path.join(__dirname, '..', 'public'),
}),
UsersModule,
JBrowseModule,
],
providers: [
{ provide: APP_GUARD, useClass: JwtAuthGuard },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import {
} from '@nestjs/common'

import { GoogleAuthGuard } from '../utils/google.guard'
import { Public } from '../utils/jwt-auth.guard'
import { MicrosoftAuthGuard } from '../utils/microsoft.guard'
import {
AuthenticationService,
RequestWithUserToken,
} from './authentication.service'
import { Validations } from '../utils/validation/validatation.decorator'
import { Role } from '../utils/role/role.enum'

@Public()
@Validations(Role.None)
@Controller('auth')
export class AuthenticationController {
private readonly logger = new Logger(AuthenticationController.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ interface ConfigValues {
GOOGLE_CLIENT_ID?: string
GOOGLE_CLIENT_ID_FILE?: string
ALLOW_GUEST_USER: boolean
DEFAULT_NEW_USER_ROLE: Role | 'none'
DEFAULT_NEW_USER_ROLE: Role
ROOT_USER_NAME: string
ROOT_USER_PASSWORD: string
}

@Injectable()
export class AuthenticationService {
private readonly logger = new Logger(AuthenticationService.name)
private defaultNewUserRole: Role | 'none'
private defaultNewUserRole: Role

constructor(
private readonly usersService: UsersService,
Expand Down Expand Up @@ -183,9 +183,10 @@ export class AuthenticationService {
// If there is not a non-guest user yet, the 1st user role will be admin
newUserRole = hasAdmin ? this.defaultNewUserRole : Role.Admin
}
const newUser: CreateUserDto = { email, username: name }
if (newUserRole !== 'none') {
newUser.role = newUserRole
const newUser: CreateUserDto = {
email,
username: name,
role: newUserRole,
}
user = await this.usersService.addNew(newUser)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AssembliesModule } from '../assemblies/assemblies.module'
import { CountersModule } from '../counters/counters.module'
import { FeaturesModule } from '../features/features.module'
import { FilesModule } from '../files/files.module'
import { JBrowseModule } from '../jbrowse/jbrowse.module'
import { MessagesModule } from '../messages/messages.module'
import { RefSeqChunksModule } from '../refSeqChunks/refSeqChunks.module'
import { RefSeqsModule } from '../refSeqs/refSeqs.module'
Expand Down Expand Up @@ -37,6 +38,7 @@ import { ChangesService } from './changes.service'
UsersModule,
CountersModule,
MessagesModule,
JBrowseModule,
],
})
export class ChangesModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
RefSeqChunk,
RefSeqChunkDocument,
RefSeqDocument,
JBrowseConfig,
JBrowseConfigDocument,
User,
UserDocument,
} from '@apollo-annotation/schemas'
Expand Down Expand Up @@ -54,6 +56,8 @@ export class ChangesService {
private readonly fileModel: Model<FileDocument>,
@InjectModel(User.name)
private readonly userModel: Model<UserDocument>,
@InjectModel(JBrowseConfig.name)
private readonly jbrowseConfigModel: Model<JBrowseConfigDocument>,
@InjectModel(Change.name)
private readonly changeModel: Model<ChangeDocument>,
private readonly filesService: FilesService,
Expand Down Expand Up @@ -110,6 +114,7 @@ export class ChangesService {
refSeqChunkModel: this.refSeqChunkModel,
fileModel: this.fileModel,
userModel: this.userModel,
jbrowseConfigModel: this.jbrowseConfigModel,
session,
filesService: this.filesService,
counterService: this.countersService,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Controller, Get, Logger, Param, Query } from '@nestjs/common'

import { FeatureRangeSearchDto } from '../entity/gff3Object.dto'
import { Public } from '../utils/jwt-auth.guard'
import { Role } from '../utils/role/role.enum'
import { Validations } from '../utils/validation/validatation.decorator'
import { ChecksService } from './checks.service'

@Public()
@Validations(Role.ReadOnly)
@Controller('checks')
export class ChecksController {
constructor(private readonly checksService: ChecksService) {}
Expand All @@ -28,7 +27,6 @@ export class ChecksController {
* @param searchDto - range
* @returns an array of checkResult -documents
*/
@Validations(Role.ReadOnly)
@Get('range')
getFeatures(@Query() request: FeatureRangeSearchDto) {
this.logger.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from '@nestjs/common'
import { Response as ExpressResponse } from 'express'

import { Public } from '../utils/jwt-auth.guard'
import { Role } from '../utils/role/role.enum'
import { Validations } from '../utils/validation/validatation.decorator'
import { ExportService } from './export.service'
Expand Down Expand Up @@ -39,7 +38,7 @@ export class ExportController {
* @param res -
* @returns A StreamableFile of the GFF3
*/
@Public()
@Validations(Role.None)
@Get()
async exportGFF3(
@Query() request: { exportID: string; fastaWidth?: number },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Controller, Get, Logger, Param, Query } from '@nestjs/common'

import { FeatureRangeSearchDto } from '../entity/gff3Object.dto'
import { Public } from '../utils/jwt-auth.guard'
import { Role } from '../utils/role/role.enum'
import { Validations } from '../utils/validation/validatation.decorator'
import { FeatureCountRequest } from './dto/feature.dto'
import { FeaturesService } from './features.service'

@Validations(Role.ReadOnly)
@Controller('features')
export class FeaturesController {
constructor(private readonly featuresService: FeaturesService) {}
Expand All @@ -17,7 +17,6 @@ export class FeaturesController {
* For testing try to go to:
* http://localhost:3999/features/searchFeatures?term=exonerate
*/
@Public()
@Get('searchFeatures')
async searchFeatures(@Query() request: { term: string; assemblies: string }) {
return this.featuresService.searchFeatures(request)
Expand All @@ -29,7 +28,6 @@ export class FeaturesController {
* @returns Return 'HttpStatus.OK' and array of features if search was successful
* or if search data was not found or in case of error throw exception
*/
@Validations(Role.ReadOnly)
@Get('getFeatures')
getFeatures(@Query() request: FeatureRangeSearchDto) {
this.logger.debug(
Expand All @@ -39,7 +37,6 @@ export class FeaturesController {
return this.featuresService.findByRange(request)
}

@Validations(Role.ReadOnly)
@Get('count')
async getFeatureCount(@Query() featureCountRequest: FeatureCountRequest) {
this.logger.debug(
Expand All @@ -56,14 +53,12 @@ export class FeaturesController {
* @returns Return 'HttpStatus.OK' and the feature(s) if search was successful
* or if search data was not found or in case of error throw exception
*/
@Validations(Role.ReadOnly)
@Get(':featureid')
getFeature(@Param('featureid') featureid: string) {
this.logger.debug(`Get feature by featureId: ${featureid}`)
return this.featuresService.findById(featureid)
}

@Public()
@Get('check/:featureid')
checkFeature(@Param('featureid') featureid: string) {
return this.featuresService.checkFeature(featureid)
Expand All @@ -74,7 +69,6 @@ export class FeaturesController {
* @returns Return 'HttpStatus.OK' and array of features if search was successful
* or if search data was not found or in case of error throw exception
*/
@Validations(Role.ReadOnly)
@Get()
getAll() {
this.logger.debug('Get all features')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Controller, Get } from '@nestjs/common'
import { HealthCheck, HealthCheckService } from '@nestjs/terminus'

import { Public } from '../utils/jwt-auth.guard'
import { Validations } from '../utils/validation/validatation.decorator'
import { Role } from '../utils/role/role.enum'

@Controller('health')
export class HealthController {
constructor(private health: HealthCheckService) {}

@Public()
@Validations(Role.None)
@Get()
@HealthCheck()
check() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class CreateTrackDto {
readonly type: string
readonly trackId: string
readonly config: string[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Test, TestingModule } from '@nestjs/testing'

import { JBrowseController } from './jbrowse.controller'

describe('JBrowseController', () => {
let controller: JBrowseController

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [JBrowseController],
}).compile()

controller = module.get<JBrowseController>(JBrowseController)
})

it('should be defined', () => {
expect(controller).toBeDefined()
})
})
Loading

0 comments on commit 6fbe4ac

Please sign in to comment.