@graphql-tools/prisma-loader
Version:
A set of utils for faster development of GraphQL tools
298 lines (297 loc) • 11.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.concatName = exports.PrismaDefinitionClass = void 0;
const tslib_1 = require("tslib");
const yaml_js_1 = require("./yaml.js");
const fs = tslib_1.__importStar(require("fs"));
const dotenv = tslib_1.__importStar(require("dotenv"));
const path = tslib_1.__importStar(require("path"));
// eslint-disable-next-line
// @ts-ignore
const jsonwebtoken_1 = tslib_1.__importDefault(require("jsonwebtoken"));
const Cluster_js_1 = require("./Cluster.js");
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const yamlComment_js_1 = require("./utils/yamlComment.js");
const parseEndpoint_js_1 = require("./utils/parseEndpoint.js");
class PrismaDefinitionClass {
constructor(env, definitionPath, envVars = process.env, out) {
this.secrets = null;
this.definitionPath = definitionPath;
if (definitionPath) {
this.definitionDir = path.dirname(definitionPath);
}
this.env = env;
this.out = out;
this.envVars = envVars;
}
async load(args, envPath, graceful) {
if (args['project']) {
const flagPath = path.resolve(String(args['project']));
try {
fs.accessSync(flagPath);
}
catch (_a) {
throw new Error(`Prisma definition path specified by --project '${flagPath}' does not exist`);
}
this.definitionPath = flagPath;
this.definitionDir = path.dirname(flagPath);
await this.loadDefinition(args, graceful);
this.validate();
return;
}
if (envPath) {
try {
fs.accessSync(envPath);
}
catch (_b) {
envPath = path.join(process.cwd(), envPath);
}
try {
fs.accessSync(envPath);
}
catch (_c) {
throw new Error(`--env-file path '${envPath}' does not exist`);
}
}
dotenv.config({ path: envPath });
if (this.definitionPath) {
await this.loadDefinition(args, graceful);
this.validate();
}
else {
throw new Error(`Couldn’t find \`prisma.yml\` file. Are you in the right directory?`);
}
}
async loadDefinition(args, graceful) {
const { definition, rawJson } = await (0, yaml_js_1.readDefinition)(this.definitionPath, args, this.out, this.envVars, graceful);
this.rawEndpoint = rawJson.endpoint;
this.definition = definition;
this.rawJson = rawJson;
this.definitionString = fs.readFileSync(this.definitionPath, 'utf-8');
this.typesString = this.getTypesString(this.definition);
const secrets = this.definition.secret;
this.secrets = secrets ? secrets.replace(/\s/g, '').split(',') : null;
}
get endpoint() {
return (this.definition && this.definition.endpoint) || process.env['PRISMA_MANAGEMENT_API_ENDPOINT'];
}
get clusterBaseUrl() {
if (!this.definition || !this.endpoint) {
return undefined;
}
const { clusterBaseUrl } = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
return clusterBaseUrl;
}
get service() {
if (!this.definition) {
return undefined;
}
if (!this.endpoint) {
return undefined;
}
const { service } = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
return service;
}
get stage() {
if (!this.definition) {
return undefined;
}
if (!this.endpoint) {
return undefined;
}
const { stage } = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
return stage;
}
get cluster() {
if (!this.definition) {
return undefined;
}
if (!this.endpoint) {
return undefined;
}
const { clusterName } = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
return clusterName;
}
validate() {
// shared clusters need a workspace
const clusterName = this.getClusterName();
const cluster = this.env.clusterByName(clusterName);
if (this.definition &&
clusterName &&
cluster &&
cluster.shared &&
!cluster.isPrivate &&
!this.getWorkspace() &&
clusterName !== 'shared-public-demo') {
throw new Error(`Your \`cluster\` property in the prisma.yml is missing the workspace slug.
Make sure that your \`cluster\` property looks like this: ${chalk_1.default.bold('<workspace>/<cluster-name>')}. You can also remove the cluster property from the prisma.yml
and execute ${chalk_1.default.bold.green('prisma deploy')} again, to get that value auto-filled.`);
}
if (this.definition &&
this.definition.endpoint &&
clusterName &&
cluster &&
cluster.shared &&
!cluster.isPrivate &&
!this.getWorkspace() &&
clusterName !== 'shared-public-demo') {
throw new Error(`The provided endpoint ${this.definition.endpoint} points to a demo cluster, but is missing the workspace slug. A valid demo endpoint looks like this: https://eu1.prisma.sh/myworkspace/service-name/stage-name`);
}
if (this.definition && this.definition.endpoint && !this.definition.endpoint.startsWith('http')) {
throw new Error(`${chalk_1.default.bold(this.definition.endpoint)} is not a valid endpoint. It must start with http:// or https://`);
}
}
getToken(serviceName, stageName) {
if (this.secrets) {
const data = {
data: {
service: `${serviceName}@${stageName}`,
roles: ['admin'],
},
};
return jsonwebtoken_1.default.sign(data, this.secrets[0], {
expiresIn: '7d',
});
}
return undefined;
}
async getCluster(_ = false) {
if (this.definition && this.endpoint) {
const clusterData = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
const cluster = await this.getClusterByEndpoint(clusterData);
this.env.removeCluster(clusterData.clusterName);
this.env.addCluster(cluster);
return cluster;
}
return undefined;
}
findClusterByBaseUrl(baseUrl) {
var _a;
return (_a = this.env.clusters) === null || _a === void 0 ? void 0 : _a.find(c => c.baseUrl.toLowerCase() === baseUrl);
}
async getClusterByEndpoint(data) {
if (data.clusterBaseUrl && !process.env['PRISMA_MANAGEMENT_API_SECRET']) {
const cluster = this.findClusterByBaseUrl(data.clusterBaseUrl);
if (cluster) {
return cluster;
}
}
const { clusterName, clusterBaseUrl, isPrivate, local, shared, workspaceSlug } = data;
// if the cluster could potentially be served by the cloud api, fetch the available
// clusters from the cloud api
if (!local) {
await this.env.fetchClusters();
const cluster = this.findClusterByBaseUrl(data.clusterBaseUrl);
if (cluster) {
return cluster;
}
}
return new Cluster_js_1.Cluster(this.out, clusterName, clusterBaseUrl, shared || isPrivate ? this.env.cloudSessionKey : undefined, local, shared, isPrivate, workspaceSlug);
}
getTypesString(definition) {
const typesPaths = definition.datamodel
? Array.isArray(definition.datamodel)
? definition.datamodel
: [definition.datamodel]
: [];
let allTypes = '';
for (const unresolvedTypesPath of typesPaths) {
const typesPath = path.join(this.definitionDir, unresolvedTypesPath);
try {
fs.accessSync(typesPath);
const types = fs.readFileSync(typesPath, 'utf-8');
allTypes += types + '\n';
}
catch (_a) {
throw new Error(`The types definition file "${typesPath}" could not be found.`);
}
}
return allTypes;
}
getClusterName() {
return this.cluster || null;
}
getWorkspace() {
if (this.definition && this.endpoint) {
const { workspaceSlug } = (0, parseEndpoint_js_1.parseEndpoint)(this.endpoint);
if (workspaceSlug) {
return workspaceSlug;
}
}
return null;
}
async getDeployName() {
const cluster = await this.getCluster();
return concatName(cluster, this.service, this.getWorkspace());
}
getSubscriptions() {
if (this.definition && this.definition.subscriptions) {
return Object.entries(this.definition.subscriptions).map(([name, subscription]) => {
const url = typeof subscription.webhook === 'string' ? subscription.webhook : subscription.webhook.url;
const headers = typeof subscription.webhook === 'string' ? [] : transformHeaders(subscription.webhook.headers);
let query = subscription.query;
if (subscription.query.endsWith('.graphql')) {
const queryPath = path.join(this.definitionDir, subscription.query);
try {
fs.accessSync(queryPath);
}
catch (_a) {
throw new Error(`Subscription query ${queryPath} provided in subscription "${name}" in prisma.yml does not exist.`);
}
query = fs.readFileSync(queryPath, 'utf-8');
}
return {
name,
query,
headers,
url,
};
});
}
return [];
}
replaceEndpoint(newEndpoint) {
this.definitionString = (0, yamlComment_js_1.replaceYamlValue)(this.definitionString, 'endpoint', newEndpoint);
fs.writeFileSync(this.definitionPath, this.definitionString);
}
addDatamodel(datamodel) {
this.definitionString += `\ndatamodel: ${datamodel}`;
fs.writeFileSync(this.definitionPath, this.definitionString);
this.definition.datamodel = datamodel;
}
async getEndpoint(serviceInput, stageInput) {
const cluster = await this.getCluster();
const service = serviceInput || this.service;
const stage = stageInput || this.stage;
const workspace = this.getWorkspace();
if (service && stage && cluster) {
return cluster.getApiEndpoint(service, stage, workspace);
}
return null;
}
getHooks(hookType) {
if (this.definition && this.definition.hooks && this.definition.hooks[hookType]) {
const hooks = this.definition.hooks[hookType];
if (typeof hooks !== 'string' && !Array.isArray(hooks)) {
throw new Error(`Hook ${hookType} provided in prisma.yml must be string or an array of strings.`);
}
return typeof hooks === 'string' ? [hooks] : hooks;
}
return [];
}
}
exports.PrismaDefinitionClass = PrismaDefinitionClass;
function concatName(cluster, name, workspace) {
if (cluster.shared) {
const workspaceString = workspace ? `${workspace}~` : '';
return `${workspaceString}${name}`;
}
return name;
}
exports.concatName = concatName;
function transformHeaders(headers) {
if (!headers) {
return [];
}
return Object.entries(headers).map(([name, value]) => ({ name, value }));
}