Files
kami-parse-server/src/Controllers/index.js
Omair Vaiyani d3810c2eba GraphQL Configuration Options (#5782)
* add parse-graph-ql configuration for class schema customisation

Not yet tested - essentially an RFC

* refactor and add graphql router, controller and config cache

* fix(GraphQLController): add missing check isEnabled

* chore(GraphQLController): remove awaits from cache put

* chore(GraphQLController): remove check for if its enabled

* refactor(GraphQLController): only use cache if mounted

* chore(GraphQLController): group all validation errors and throw at once

* chore(GraphQLSchema): move transformations into controller validation

* refactor(GraphQL): improve ctrl validation and fix schema usage of config

* refactor(GraphQLSchema): remove code related to additional schema

This code has been moved into a separate feature branch.

* fix(GraphQLSchema): fix incorrect default return type for class configs

* refactor(GraphQLSchema): update staleness check code to account for config

* fix(GraphQLServer): fix regressed tests due to internal schema changes

This will be followed up with a backwards compatability fix for the `ClassFields` issue to avoid breakages for our users

* refactor: rename to ParseGraphQLController for consistency

* fix(ParseGraphQLCtrl): numerous fixes for validity checking

Also includes some minor code refactoring

* chore(GraphQL): minor syntax cleanup

* fix(SchemaController): add _GraphQLConfig to volatile classes

* refactor(ParseGraphQLServer): return update config value in setGraphQLConfig

* testing(ParseGraphQL): add test cases for new graphQLConfig

* fix(GraphQLController): fix issue where config with multiple items was not being mapped to the db

* fix(postgres): add _GraphQLConfig default schema on load

fixes failing postgres tests

* GraphQL @mock directive (#5836)

* Add mock directive
* Include tests for @mock directive

* Fix existing tests due to the change from ClassFields to ClassCreateFields

* fix(parseClassMutations): safer type transformation based on input type

* fix(parseClassMutations): only define necessary input fields

* fix(GraphQL): fix incorrect import paths
2019-07-25 12:46:25 -07:00

281 lines
8.2 KiB
JavaScript

import authDataManager from '../Adapters/Auth';
import { ParseServerOptions } from '../Options';
import { loadAdapter } from '../Adapters/AdapterLoader';
import defaults from '../defaults';
import url from 'url';
// Controllers
import { LoggerController } from './LoggerController';
import { FilesController } from './FilesController';
import { HooksController } from './HooksController';
import { UserController } from './UserController';
import { CacheController } from './CacheController';
import { LiveQueryController } from './LiveQueryController';
import { AnalyticsController } from './AnalyticsController';
import { PushController } from './PushController';
import { PushQueue } from '../Push/PushQueue';
import { PushWorker } from '../Push/PushWorker';
import DatabaseController from './DatabaseController';
import SchemaCache from './SchemaCache';
// Adapters
import { GridFSBucketAdapter } from '../Adapters/Files/GridFSBucketAdapter';
import { WinstonLoggerAdapter } from '../Adapters/Logger/WinstonLoggerAdapter';
import { InMemoryCacheAdapter } from '../Adapters/Cache/InMemoryCacheAdapter';
import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter';
import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter';
import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorageAdapter';
import ParsePushAdapter from '@parse/push-adapter';
import ParseGraphQLController from './ParseGraphQLController';
export function getControllers(options: ParseServerOptions) {
const loggerController = getLoggerController(options);
const filesController = getFilesController(options);
const userController = getUserController(options);
const {
pushController,
hasPushScheduledSupport,
hasPushSupport,
pushControllerQueue,
pushWorker,
} = getPushController(options);
const cacheController = getCacheController(options);
const analyticsController = getAnalyticsController(options);
const liveQueryController = getLiveQueryController(options);
const databaseController = getDatabaseController(options, cacheController);
const hooksController = getHooksController(options, databaseController);
const authDataManager = getAuthDataManager(options);
const parseGraphQLController = getParseGraphQLController(options, {
databaseController,
cacheController,
});
return {
loggerController,
filesController,
userController,
pushController,
hasPushScheduledSupport,
hasPushSupport,
pushWorker,
pushControllerQueue,
analyticsController,
cacheController,
parseGraphQLController,
liveQueryController,
databaseController,
hooksController,
authDataManager,
};
}
export function getLoggerController(
options: ParseServerOptions
): LoggerController {
const {
appId,
jsonLogs,
logsFolder,
verbose,
logLevel,
silent,
loggerAdapter,
} = options;
const loggerOptions = { jsonLogs, logsFolder, verbose, logLevel, silent };
const loggerControllerAdapter = loadAdapter(
loggerAdapter,
WinstonLoggerAdapter,
loggerOptions
);
return new LoggerController(loggerControllerAdapter, appId, loggerOptions);
}
export function getFilesController(
options: ParseServerOptions
): FilesController {
const {
appId,
databaseURI,
filesAdapter,
databaseAdapter,
preserveFileName,
} = options;
if (!filesAdapter && databaseAdapter) {
throw 'When using an explicit database adapter, you must also use an explicit filesAdapter.';
}
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
return new GridFSBucketAdapter(databaseURI);
});
return new FilesController(filesControllerAdapter, appId, {
preserveFileName,
});
}
export function getUserController(options: ParseServerOptions): UserController {
const { appId, emailAdapter, verifyUserEmails } = options;
const emailControllerAdapter = loadAdapter(emailAdapter);
return new UserController(emailControllerAdapter, appId, {
verifyUserEmails,
});
}
export function getCacheController(
options: ParseServerOptions
): CacheController {
const { appId, cacheAdapter, cacheTTL, cacheMaxSize } = options;
const cacheControllerAdapter = loadAdapter(
cacheAdapter,
InMemoryCacheAdapter,
{ appId: appId, ttl: cacheTTL, maxSize: cacheMaxSize }
);
return new CacheController(cacheControllerAdapter, appId);
}
export function getParseGraphQLController(
options: ParseServerOptions,
controllerDeps
): ParseGraphQLController {
return new ParseGraphQLController({
mountGraphQL: options.mountGraphQL,
...controllerDeps,
});
}
export function getAnalyticsController(
options: ParseServerOptions
): AnalyticsController {
const { analyticsAdapter } = options;
const analyticsControllerAdapter = loadAdapter(
analyticsAdapter,
AnalyticsAdapter
);
return new AnalyticsController(analyticsControllerAdapter);
}
export function getLiveQueryController(
options: ParseServerOptions
): LiveQueryController {
return new LiveQueryController(options.liveQuery);
}
export function getDatabaseController(
options: ParseServerOptions,
cacheController: CacheController
): DatabaseController {
const {
databaseURI,
databaseOptions,
skipMongoDBServer13732Workaround,
collectionPrefix,
schemaCacheTTL,
enableSingleSchemaCache,
} = options;
let { databaseAdapter } = options;
if (
(databaseOptions ||
(databaseURI && databaseURI !== defaults.databaseURI) ||
collectionPrefix !== defaults.collectionPrefix) &&
databaseAdapter
) {
throw 'You cannot specify both a databaseAdapter and a databaseURI/databaseOptions/collectionPrefix.';
} else if (!databaseAdapter) {
databaseAdapter = getDatabaseAdapter(
databaseURI,
collectionPrefix,
databaseOptions
);
} else {
databaseAdapter = loadAdapter(databaseAdapter);
}
return new DatabaseController(
databaseAdapter,
new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache),
skipMongoDBServer13732Workaround
);
}
export function getHooksController(
options: ParseServerOptions,
databaseController: DatabaseController
): HooksController {
const { appId, webhookKey } = options;
return new HooksController(appId, databaseController, webhookKey);
}
interface PushControlling {
pushController: PushController;
hasPushScheduledSupport: boolean;
pushControllerQueue: PushQueue;
pushWorker: PushWorker;
}
export function getPushController(
options: ParseServerOptions
): PushControlling {
const { scheduledPush, push } = options;
const pushOptions = Object.assign({}, push);
const pushQueueOptions = pushOptions.queueOptions || {};
if (pushOptions.queueOptions) {
delete pushOptions.queueOptions;
}
// Pass the push options too as it works with the default
const pushAdapter = loadAdapter(
pushOptions && pushOptions.adapter,
ParsePushAdapter,
pushOptions
);
// We pass the options and the base class for the adatper,
// Note that passing an instance would work too
const pushController = new PushController();
const hasPushSupport = !!(pushAdapter && push);
const hasPushScheduledSupport = hasPushSupport && scheduledPush === true;
const { disablePushWorker } = pushQueueOptions;
const pushControllerQueue = new PushQueue(pushQueueOptions);
let pushWorker;
if (!disablePushWorker) {
pushWorker = new PushWorker(pushAdapter, pushQueueOptions);
}
return {
pushController,
hasPushSupport,
hasPushScheduledSupport,
pushControllerQueue,
pushWorker,
};
}
export function getAuthDataManager(options: ParseServerOptions) {
const { auth, enableAnonymousUsers } = options;
return authDataManager(auth, enableAnonymousUsers);
}
export function getDatabaseAdapter(
databaseURI,
collectionPrefix,
databaseOptions
) {
let protocol;
try {
const parsedURI = url.parse(databaseURI);
protocol = parsedURI.protocol ? parsedURI.protocol.toLowerCase() : null;
} catch (e) {
/* */
}
switch (protocol) {
case 'postgres:':
return new PostgresStorageAdapter({
uri: databaseURI,
collectionPrefix,
databaseOptions,
});
default:
return new MongoStorageAdapter({
uri: databaseURI,
collectionPrefix,
mongoOptions: databaseOptions,
});
}
}