UNPKG

realm-object-server

Version:

Realm Object Server

919 lines 40.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const apollo_server_express_1 = require("apollo-server-express"); const express = require("express"); const graphql_1 = require("graphql"); const graphql_subscriptions_1 = require("graphql-subscriptions"); const graphql_tools_1 = require("graphql-tools"); const LRU = require("lru-cache"); const moment = require("moment"); const pluralize = require("pluralize"); const realmUtil_1 = require("../shared/realmUtil"); const subscriptions_transport_ws_1 = require("subscriptions-transport-ws"); const timers_1 = require("timers"); const url = require("url"); const uuid_1 = require("uuid"); const Token_1 = require("../shared/Token"); const decorators_1 = require("../decorators"); const errors = require("../errors"); const AdminRealm_1 = require("../realms/AdminRealm"); const util_1 = require("../shared/util"); const ConfigurableServiceBase_1 = require("./ConfigurableServiceBase"); const RealmProblem_1 = require("../errors/RealmProblem"); let GraphQLService = class GraphQLService extends ConfigurableServiceBase_1.ConfigurableServiceBase { constructor(config = {}) { super(config); this.schemaHandlers = {}; this.querySubscriptions = {}; this.linkingObjectsResolver = { _linkingObjects: (parent, args) => { let result = parent.linkingObjects(args.objectType, args.property); if (args.query) { result = result.filtered(args.query); } return this.getCollectionResponse(result, args); }, }; this.subscriptionObjectNameRegex = /^(class_)?(.*?)(_matches)?$/gm; } setConfigCore(config) { if (config.schemaCacheSettings !== "NoCache") { this.schemaCache = new LRU({ max: (config.schemaCacheSettings && config.schemaCacheSettings.max) || 1000, maxAge: config.schemaCacheSettings && config.schemaCacheSettings.maxAge, }); } this.disableAuthentication = config.disableAuthentication || false; this.disableExplorer = config.disableExplorer || false; this.realmCacheTTL = config.realmCacheMaxAge || 120000; this.forceExplorerSSL = config.forceExplorerSSL; this.includeCountInResponses = config.includeCountInResponses || false; this.presentIntsAsFloatsInSchema = config.presentIntsAsFloatsInSchema || false; this.collectionModelSuffix = config.collectionModelSuffix || "Collection"; this.namedSubscriptionModelName = config.namedSubscriptionModelName || "NamedSubscription"; this.inputModelSuffix = config.inputModelSuffix || "Input"; this.allRealmTypesName = config.allRealmTypesModelName || "AllRealmTypes"; this.updatePolicyModelName = config.updatePolicyModelName || "UpdatePolicy"; this.base64Type = new graphql_1.GraphQLScalarType({ name: config.base64ModelName || "Base64", description: "A base64-encoded binary blob", serialize(value) { return Buffer.from(value).toString("base64"); }, parseValue(value) { return Buffer.from(value, "base64"); }, parseLiteral(ast) { if (ast.kind === "StringValue") { return Buffer.from(ast.value, "base64"); } throw new TypeError(`Expected StringValue literal, but got ${ast.kind}`); }, }); this.dateType = new graphql_1.GraphQLScalarType({ name: config.dateModelName || "Date", description: "An ISO 8601 represantation of a date value", serialize(value) { return value.toJSON(); }, parseValue(value) { return new Date(value); }, parseLiteral(ast) { if (ast.kind === "StringValue") { return new Date(ast.value); } throw new TypeError(`Expected StringValue literal, but got ${ast.kind}`); }, }); } startCore(server) { return __awaiter(this, void 0, void 0, function* () { this.pubsub = new graphql_subscriptions_1.PubSub(); const getOperationId = (socket, messageId) => { return `${socket.id}_${messageId}`; }; this.subscriptionServer = new subscriptions_transport_ws_1.SubscriptionServer({ execute: graphql_1.execute, subscribe: graphql_1.subscribe, onOperationComplete: (socket, messageId) => { const opid = getOperationId(socket, messageId); const details = this.querySubscriptions[opid]; if (details) { details.results.removeAllListeners(); this.closeRealm(details.realm); delete this.querySubscriptions[opid]; } this.metrics.activeSubscriptions.dec({}); }, onOperation: (message, params, socket) => __awaiter(this, void 0, void 0, function* () { if (!socket.realmPath) { throw new graphql_1.GraphQLError('Missing "realmPath" from context. It is required for subscriptions.'); } const context = params.context; context.operationId = getOperationId(socket, message.id); context.realm = yield this.openRealm(socket.realmPath, context.user); params.schema = this.getSchema(socket.realmPath, context.realm); this.metrics.activeSubscriptions.inc({}); return params; }), onConnect: (authPayload, socket) => __awaiter(this, void 0, void 0, function* () { let accessToken; let user; if (!this.disableAuthentication) { if (!authPayload || !authPayload.token) { throw new errors.realm.MissingParameters("Missing 'connectionParams.token'."); } accessToken = this.server.tokenValidator.parse(authPayload.token); user = yield this.authenticate(accessToken, socket.realmPath); } if (!socket.id) { socket.id = uuid_1.v4(); this.metrics.openWebsockets.inc({}); } return { accessToken, user, }; }), onDisconnect: (socket, context) => { if (socket.id) { this.metrics.openWebsockets.dec({}); } } }, { noServer: true, }); this.handler = apollo_server_express_1.graphqlExpress((req, res) => __awaiter(this, void 0, void 0, function* () { this.metrics.totalRequests.inc({}); let realm; res.once("finish", () => { this.closeRealm(realm); let contentLength = res["_contentLength"]; if (typeof contentLength === "undefined") { const header = res.getHeader("content-length"); if (header !== null) { contentLength = parseInt(header); } } if (typeof contentLength === "number") { this.metrics.responseSize.observe({}, contentLength); } }); const path = this.getPath(req); realm = yield this.openRealm(path, req.user); const schema = this.getSchema(path, realm); const result = { schema, context: { realm, accessToken: req.authToken, user: req.user, }, }; return result; })); this.graphiql = apollo_server_express_1.graphiqlExpress((req) => { const path = this.getPath(req); let protocol; switch (this.forceExplorerSSL) { case true: protocol = "wss"; break; case false: protocol = "ws"; break; default: protocol = req.protocol === "https" ? "wss" : "ws"; break; } const result = { endpointURL: `/graphql/${encodeURIComponent(path)}`, subscriptionsEndpoint: `${protocol}://${req.get("host")}/graphql/${encodeURIComponent(path)}`, }; const token = req.get("authorization"); if (token) { result.passHeader = `'Authorization': '${token}'`; result.websocketConnectionParams = { token }; } return result; }); this.adminRealm = yield server.openRealm(AdminRealm_1.AdminRealm); this.metrics = { totalRequests: this.stats.counter({ name: "ros_graphql_requests_total", help: "Counter for all GraphQL requests", }), responseSize: this.stats.histogram({ name: "ros_graphql_response_size_bytes", help: "HTTP response body sizes of the GraphQL service", buckets: [1, 25, 50, 100, 250, 500, 1024].map(v => v * 1024), }), activeSubscriptions: this.stats.gauge({ name: "ros_graphql_active_subscriptions", help: "Number of active GraphQL subscriptions", }), openWebsockets: this.stats.gauge({ name: "ros_graphql_open_websockets", help: "Number of open websockets to the GraphQL service", }), }; }); } stopCore() { return __awaiter(this, void 0, void 0, function* () { if (this.adminRealm) { this.adminRealm.close(); delete this.adminRealm; } this.subscriptionServer.close(); }); } subscriptionHandler(req, socket, head) { return __awaiter(this, void 0, void 0, function* () { const wsServer = this.subscriptionServer.server; const ws = yield new Promise((resolve) => wsServer.handleUpgrade(req, socket, head, resolve)); const path = url.parse(req.url).path.replace("/graphql/", ""); ws.realmPath = this.getPath(path); wsServer.emit("connection", ws, req); }); } getExplore(req, res, next) { return __awaiter(this, void 0, void 0, function* () { if (this.disableExplorer) { throw new errors.realm.AccessDenied(); } yield this.authenticateRequest(req); this.graphiql(req, res, next); }); } postExplore(req, res, next) { return __awaiter(this, void 0, void 0, function* () { if (this.disableExplorer) { throw new errors.realm.AccessDenied(); } yield this.authenticateRequest(req); this.graphiql(req, res, next); }); } get(req, res, next) { return __awaiter(this, void 0, void 0, function* () { yield this.authenticateRequest(req); this.handler(req, res, next); }); } post(req, res, next) { return __awaiter(this, void 0, void 0, function* () { yield this.authenticateRequest(req); this.handler(req, res, next); }); } deleteSchema(req, res) { return __awaiter(this, void 0, void 0, function* () { yield this.authenticateRequest(req); this.schemaCache.del(this.getPath(req)); res.status(204).send({}); }); } getPath(reqOrPath) { let path = typeof reqOrPath === "string" ? decodeURIComponent(reqOrPath) : reqOrPath.params["0"]; if (!path.startsWith("/")) { path = `/${path}`; } return path; } authenticateRequest(req) { return __awaiter(this, void 0, void 0, function* () { req.user = yield this.authenticate(req.authToken, this.getPath(req)); }); } authenticate(authToken, path) { return __awaiter(this, void 0, void 0, function* () { if (this.disableAuthentication) { return undefined; } if (!authToken) { throw new errors.realm.AccessDenied({ detail: "Authorization header is missing." }); } const accessToken = authToken; if (!this.server.tokenValidator.isAdminToken(authToken) && (!path || accessToken.path !== path)) { throw new errors.realm.InvalidCredentials({ detail: "The access token doesn't grant access to the requested path." }); } const partialInfo = util_1.extractPartialInfo(path); if (!partialInfo.isPartial) { return undefined; } if (!partialInfo.customIdentifier.startsWith(authToken.identity + "/")) { throw new errors.realm.InvalidCredentials({ detail: "The identifier after /__partial/ in the route must match the user Id. Expected: " + `'/__partial/${authToken.identity}/*', but got '/__partial/${partialInfo.customIdentifier}'.`, }); } if (!this.adminRealm) { throw new errors.realm.ServiceUnavailable(); } const adminRealmUser = this.adminRealm.objectForPrimaryKey("User", authToken.identity); const refreshToken = new Token_1.RefreshToken({ appId: authToken.appId, identity: authToken.identity, isAdmin: (adminRealmUser && adminRealmUser.isAdmin) || false, expires: moment().add(1, "year").unix(), }); const authService = yield this.server.discovery.waitForService("auth"); const user = realmUtil_1.Realm.Sync.User.deserialize({ identity: refreshToken.identity, isAdmin: refreshToken.isAdmin, refreshToken: refreshToken.sign(this.server.privateKey), server: `http://${authService.address}:${authService.port}`, }); return user; }); } validateAccess(context, access) { if (this.disableAuthentication || this.server.tokenValidator.isAdminToken(context.accessToken)) { return; } const token = context.accessToken; if (!token || !token.access || token.access.indexOf(access) < 0) { throw new errors.realm.InvalidCredentials({ title: `The current user doesn't have '${access}' access.`, }); } } closeRealm(realm) { if (!realm) { return; } if (this.realmCacheTTL >= 0) { timers_1.setTimeout(() => realm.close(), this.realmCacheTTL); } else { realm.close(); } } validateRead(context) { this.validateAccess(context, "download"); } validateWrite(context) { this.validateAccess(context, "upload"); } getSchema(path, realm) { if (this.schemaCache && this.schemaCache.has(path)) { return this.schemaCache.get(path); } let schema = ` scalar ${this.base64Type.name} scalar ${this.dateType.name} enum ${this.updatePolicyModelName} { NEVER MODIFIED ALL } `; const types = new Array(); const queryResolver = {}; const mutationResolver = {}; const subscriptionResolver = {}; const extraResolvers = {}; const partialInfo = util_1.extractPartialInfo(path); for (const obj of realm.schema) { if (this.isReserved(obj.name)) { continue; } const propertyInfo = this.getPropertySchema(obj); if (!propertyInfo.propertySchema) { continue; } types.push([obj.name, propertyInfo]); } for (const [type, propertyInfo] of types) { schema += `type ${type} { ${propertyInfo.propertySchema} _linkingObjects(objectType: String!, property: String!, query: String, skip: Int, take: Int): ${this.getCollectionType(this.allRealmTypesName)} }\n\n`; schema += `type ${type}${this.collectionModelSuffix} { count: Int! items: [${type}!] }\n`; schema += `input ${type}${this.inputModelSuffix} { \n${propertyInfo.inputPropertySchema}}\n\n`; } if (types.length === 0) { throw new Error(`The schema for Realm at path ${path} is empty.`); } const allTypesUnion = types.map(([type, _]) => type).join(" | "); const allTypesCollectionUnion = types.map(([type, _]) => `${type}${this.collectionModelSuffix}`).join(" | "); schema += `union ${this.allRealmTypesName} = ${allTypesUnion}\n`; schema += `union ${this.allRealmTypesName}${this.collectionModelSuffix} = ${allTypesCollectionUnion}\n`; let query = "type Query {\n"; let mutation = "type Mutation {\n"; let subscription = "type Subscription {\n"; if (partialInfo.isPartial) { schema += `type ${this.namedSubscriptionModelName} { name: String objectType: String! query: String! createdAt: Date updatedAt: Date expiresAt: Date timeToLive: Float }\n`; schema += `type ${this.namedSubscriptionModelName}${this.collectionModelSuffix} { count: Int! items: [${this.namedSubscriptionModelName}!] }\n`; query += this.setupListPartialSubscriptions(queryResolver); mutation += this.setupDeletePartialSubscription(mutationResolver); } for (const [type, propertyInfo] of types) { const camelCasedType = this.camelcase(type); const pluralType = this.pluralize(camelCasedType); query += this.setupGetAllObjects(queryResolver, type, pluralType); mutation += this.setupAddObject(mutationResolver, type); mutation += this.setupDeleteObjects(mutationResolver, type); mutation += this.setupCreateObject(mutationResolver, type); mutation += this.setupCreateObjects(mutationResolver, type); if (partialInfo.isPartial) { mutation += this.setupCreatePartialSubscription(mutationResolver, type); } subscription += this.setupSubscribeToQuery(subscriptionResolver, type, pluralType); if (propertyInfo.pk) { query += this.setupGetObjectByPK(queryResolver, type, camelCasedType, propertyInfo.pk); mutation += this.setupUpdateObject(mutationResolver, type); mutation += this.setupDiffUpdateObject(mutationResolver, type); mutation += this.setupDeleteObject(mutationResolver, type, propertyInfo.pk); } extraResolvers[type] = this.linkingObjectsResolver; } query += "}\n\n"; mutation += "}\n\n"; subscription += "}"; schema += query; schema += mutation; schema += subscription; extraResolvers[this.allRealmTypesName] = { __resolveType: (_, __, info) => { return this.resolveLinkingObjectsTypeArgument(info); } }; extraResolvers[`${this.allRealmTypesName}${this.collectionModelSuffix}`] = { __resolveType: (_, __, info) => { return `${this.resolveLinkingObjectsTypeArgument(info)}${this.collectionModelSuffix}`; } }; const result = graphql_tools_1.makeExecutableSchema({ typeDefs: schema, resolvers: Object.assign({ Query: queryResolver, Mutation: mutationResolver, Subscription: subscriptionResolver, [this.base64Type.name]: this.base64Type, [this.dateType.name]: this.dateType }, extraResolvers), }); if (this.schemaCache) { this.schemaCache.set(path, result); } return result; } setupGetAllObjects(queryResolver, type, pluralType) { queryResolver[pluralType] = (_, args, context) => { this.validateRead(context); let result = context.realm.objects(type); if (args.query) { result = result.filtered(args.query); } if (args.sortBy) { const descending = args.descending || false; result = result.sorted(args.sortBy, descending); } return this.getCollectionResponse(result, args); }; return `${pluralType}(query: String, sortBy: String, descending: Boolean, skip: Int, take: Int): ${this.getCollectionType(type)}\n`; } setupAddObject(mutationResolver, type) { mutationResolver[`add${type}`] = (_, args, context) => { this.validateWrite(context); return this.createObjects(context.realm, type, [args.input], "NEVER")[0]; }; return `add${type}(input: ${type}${this.inputModelSuffix}): ${type} @deprecated(reason: "Use create${type} with 'updatePolicy: NEVER'")\n`; } setupUpdateObject(mutationResolver, type) { mutationResolver[`update${type}`] = (_, args, context) => { this.validateWrite(context); return this.createObjects(context.realm, type, [args.input], "ALL")[0]; }; return `update${type}(input: ${type}${this.inputModelSuffix}): ${type} @deprecated(reason: "Use create${type} with 'updatePolicy: ALL'")\n`; } setupDiffUpdateObject(mutationResolver, type) { mutationResolver[`diffUpdate${type}`] = (_, args, context) => { this.validateWrite(context); return this.createObjects(context.realm, type, [args.input], "MODIFIED")[0]; }; return `diffUpdate${type}(input: ${type}${this.inputModelSuffix}): ${type} @deprecated(reason: "Use create${type} with 'updatePolicy: MODIFIED'")\n`; } setupCreateObject(mutationResolver, type) { mutationResolver[`create${type}`] = (_, args, context) => { this.validateWrite(context); return this.createObjects(context.realm, type, [args.input], args.updatePolicy)[0]; }; return `create${type}(input: ${type}${this.inputModelSuffix}!, updatePolicy: ${this.updatePolicyModelName}): ${type}!\n`; } setupCreateObjects(mutationResolver, type) { const pluralType = this.pluralize(type); mutationResolver[`create${pluralType}`] = (_, args, context) => { this.validateWrite(context); return this.createObjects(context.realm, type, args.input, args.updatePolicy); }; return `create${pluralType}(input: [${type}${this.inputModelSuffix}!], updatePolicy: ${this.updatePolicyModelName}): [${type}!]\n`; } setupSubscribeToQuery(subscriptionResolver, type, pluralType) { subscriptionResolver[pluralType] = { subscribe: (_, args, context) => { this.validateRead(context); let result = context.realm.objects(type); if (args.query) { result = result.filtered(args.query); } if (args.sortBy) { const descending = args.descending || false; result = result.sorted(args.sortBy, descending); } const opId = context.operationId; this.querySubscriptions[opId] = { results: result, realm: context.realm, }; result.addListener((collection, change) => { const payload = {}; payload[pluralType] = this.getCollectionResponse(collection, args); this.pubsub.publish(opId, payload); }); return this.pubsub.asyncIterator(opId); }, }; return `${pluralType}(query: String, sortBy: String, descending: Boolean, skip: Int, take: Int): ${this.getCollectionType(type)}\n`; } setupGetObjectByPK(queryResolver, type, camelCasedType, pk) { queryResolver[camelCasedType] = (_, args, context) => { this.validateRead(context); return context.realm.objectForPrimaryKey(type, args[pk.name]); }; return `${camelCasedType}(${pk.name}: ${pk.type}): ${type}\n`; } setupDeleteObject(mutationResolver, type, pk) { mutationResolver[`delete${type}`] = (_, args, context) => { this.validateWrite(context); let result = false; context.realm.write(() => { const obj = context.realm.objectForPrimaryKey(type, args[pk.name]); if (obj) { context.realm.delete(obj); result = true; } }); return result; }; return `delete${type}(${pk.name}: ${pk.type}): Boolean\n`; } setupDeleteObjects(mutationResolver, type) { const pluralType = this.pluralize(type); mutationResolver[`delete${pluralType}`] = (_, args, context) => { this.validateWrite(context); let result; context.realm.write(() => { let toDelete = context.realm.objects(type); if (args.query) { toDelete = toDelete.filtered(args.query); } result = toDelete.length; context.realm.delete(toDelete); }); return result; }; return `delete${pluralType}(query: String): Int\n`; } setupCreatePartialSubscription(mutationResolver, type) { mutationResolver[`create${type}Subscription`] = (_, args, context) => __awaiter(this, void 0, void 0, function* () { let result = context.realm.objects(type); if (args.query) { result = result.filtered(args.query); } if (args.sortBy) { const descending = args.descending || false; result = result.sorted(args.sortBy, descending); } let subscription; if (args.name) { subscription = result.subscribe({ name: args.name, update: args.update, timeToLive: args.timeToLive, }); } else { if (args.update !== undefined || args.timeToLive !== undefined) { throw new Error("update and timeToLive are only supported when a name is provided."); } subscription = result.subscribe(); } yield new Promise((resolve, reject) => { subscription.addListener((s, state) => { switch (state) { case realmUtil_1.Realm.Sync.SubscriptionState.Complete: subscription.removeAllListeners(); resolve(); break; case realmUtil_1.Realm.Sync.SubscriptionState.Error: subscription.removeAllListeners(); reject(subscription.error); break; } }); }); return this.getCollectionResponse(result, {}); }); return `create${type}Subscription(query: String, sortBy: String, descending: Boolean, name: String, update: Boolean, timeToLive: Float): ${this.getCollectionType(type)}\n`; } setupListPartialSubscriptions(queryResolver) { queryResolver.queryBasedSubscriptions = (_, args, context) => __awaiter(this, void 0, void 0, function* () { const result = context.realm.subscriptions(args.name).map(s => { const anyS = s; return { name: s.name, objectType: anyS.matches_property.replace(this.subscriptionObjectNameRegex, "$2"), query: s.query, createdAt: anyS.created_at, updatedAt: anyS.updated_at, expiresAt: anyS.expires_at, timeToLive: anyS.time_to_live, }; }); return this.getCollectionResponse(result, {}); }); return `queryBasedSubscriptions(name: String): ${this.getCollectionType(this.namedSubscriptionModelName)}\n`; } setupDeletePartialSubscription(mutationResolver) { mutationResolver.deleteQueryBasedSubscription = (_, args, context) => __awaiter(this, void 0, void 0, function* () { context.realm.unsubscribe(args.name); return true; }); return "deleteQueryBasedSubscription(name: String!): Boolean\n"; } createObjects(realm, type, objects, updatePolicy = "NEVER") { const result = []; const updateMode = updatePolicy.toLowerCase(); realm.write(() => { for (const obj of objects) { result.push(realm.create(type, obj, updateMode)); } }); return result; } getPropertySchema(obj) { let schemaProperties = ""; let inputSchemaProperties = ""; let primaryKey = null; for (const key in obj.properties) { if (!obj.properties.hasOwnProperty(key) || this.isReserved(key)) { continue; } const prop = obj.properties[key]; if (prop.type === "linkingObjects") { continue; } const types = this.getTypeString(prop); if (!types || this.isReserved(types.type)) { continue; } schemaProperties += `${key}: ${types.type}\n`; inputSchemaProperties += `${key}: ${types.inputType}\n`; if (key === obj.primaryKey) { primaryKey = { name: key, type: types.type, }; } } return { propertySchema: schemaProperties, inputPropertySchema: inputSchemaProperties, pk: primaryKey, }; } getTypeString(prop) { let type; let inputType; switch (prop.type) { case "object": type = prop.objectType; inputType = `${prop.objectType}${this.inputModelSuffix}`; break; case "list": const innerType = this.getPrimitiveTypeString(prop.objectType, prop.optional); if (this.isReserved(innerType)) { return undefined; } type = `[${innerType}]`; inputType = this.isPrimitiveType(prop.objectType) ? type : `[${innerType}${this.inputModelSuffix}]`; break; default: type = this.getPrimitiveTypeString(prop.type, prop.optional); inputType = this.getPrimitiveTypeString(prop.type, true); break; } return { type, inputType, }; } getPrimitiveTypeString(prop, optional) { let result = ""; switch (prop) { case "bool": result = "Boolean"; break; case "int": result = this.presentIntsAsFloatsInSchema ? "Float" : "Int"; break; case "float": case "double": result = "Float"; break; case "date": result = this.dateType.name; break; case "string": result = "String"; break; case "data": result = this.base64Type.name; break; default: return prop; } if (!optional) { result += "!"; } return result; } isPrimitiveType(type) { switch (type) { case "bool": case "int": case "float": case "double": case "date": case "string": case "data": return true; default: return false; } } getCollectionType(type) { return this.includeCountInResponses ? `${type}${this.collectionModelSuffix}` : `[${type}!]`; } getCollectionResponse(collection, args) { let result = collection; if (args.skip || args.take) { const skip = args.skip || 0; const take = args.take ? (args.take + skip) : undefined; result = collection.slice(skip, take); } if (this.includeCountInResponses) { return { count: collection.length, items: result, }; } return result; } resolveLinkingObjectsTypeArgument(info) { const linkingObjectsNode = info.fieldNodes.find(n => n.name.value === "_linkingObjects"); if (!linkingObjectsNode) { throw new Error(`${this.allRealmTypesName} is only supported as the return type of _linkingObjects call.`); } const objectTypeNode = linkingObjectsNode.arguments.find(a => a.name.value === "objectType").value; return objectTypeNode.value; } camelcase(value) { return value.charAt(0).toLowerCase() + value.slice(1); } pluralize(value) { const result = pluralize(value); if (result !== value) { return result; } return result + "s"; } isReserved(value) { return value.startsWith("__"); } openRealm(path, user) { return __awaiter(this, void 0, void 0, function* () { try { const realm = yield this.server.openRealm({ remotePath: path, schema: undefined, user, forceCloseHandler: { id: "GraphQLService", handler: (deleteAfterClose) => { if (deleteAfterClose) { this.schemaCache.del(this.getPath(path)); } } } }); if (this.schemaCache) { realm.addListener("schema", this.getSchemaHandler(path)); } return realm; } catch (err) { if (err instanceof RealmProblem_1.RealmProblem) { err.title = `${err.message} (${err.status}): ${err.detail}`; } throw err; } }); } getSchemaHandler(path) { let value = this.schemaHandlers[path]; if (!value) { value = (realm, event, schema) => { try { this.schemaCache.del(path); this.getSchema(path, realm); } catch (_a) { } }; this.schemaHandlers[path] = value; } return value; } }; __decorate([ decorators_1.Upgrade("/:path+"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "subscriptionHandler", null); __decorate([ decorators_1.Get("/explore/*"), __param(0, decorators_1.Request()), __param(1, decorators_1.Response()), __param(2, decorators_1.Next()), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "getExplore", null); __decorate([ decorators_1.Post("/explore/*"), __param(0, decorators_1.Request()), __param(1, decorators_1.Response()), __param(2, decorators_1.Next()), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "postExplore", null); __decorate([ decorators_1.Get("/*"), __param(0, decorators_1.Request()), __param(1, decorators_1.Response()), __param(2, decorators_1.Next()), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "get", null); __decorate([ decorators_1.Post("/*"), __param(0, decorators_1.Request()), __param(1, decorators_1.Response()), __param(2, decorators_1.Next()), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "post", null); __decorate([ decorators_1.Delete("/schema/*"), __param(0, decorators_1.Request()), __param(1, decorators_1.Response()), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], GraphQLService.prototype, "deleteSchema", null); GraphQLService = __decorate([ decorators_1.BaseRoute("/graphql"), decorators_1.Cors("/"), __metadata("design:paramtypes", [Object]) ], GraphQLService); exports.GraphQLService = GraphQLService; //# sourceMappingURL=GraphQLService.js.map