UNPKG

@shopify/shopify-app-session-storage-prisma

Version:
196 lines (192 loc) 7 kB
'use strict'; var shopifyApi = require('@shopify/shopify-api'); var client = require('@prisma/client'); const UNIQUE_KEY_CONSTRAINT_ERROR_CODE = 'P2002'; class PrismaSessionStorage { prisma; ready; tableName = 'session'; connectionRetries = 2; connectionRetryIntervalMs = 5000; constructor(prisma, { tableName, connectionRetries, connectionRetryIntervalMs, } = {}) { this.prisma = prisma; if (tableName) { this.tableName = tableName; } if (connectionRetries !== undefined) { this.connectionRetries = connectionRetries; } if (connectionRetryIntervalMs !== undefined) { this.connectionRetryIntervalMs = connectionRetryIntervalMs; } if (this.getSessionTable() === undefined) { throw new Error(`PrismaClient does not have a ${this.tableName} table`); } this.ready = this.pollForTable() .then(() => true) .catch((cause) => { throw new MissingSessionTableError(`Prisma ${this.tableName} table does not exist. This could happen for a few reasons, see https://github.com/Shopify/shopify-app-js/tree/main/packages/apps/session-storage/shopify-app-session-storage-prisma#troubleshooting for more information`, cause); }); } async storeSession(session) { await this.ensureReady(); const data = this.sessionToRow(session); try { await this.getSessionTable().upsert({ where: { id: session.id }, update: data, create: data, }); } catch (error) { if (error instanceof client.Prisma.PrismaClientKnownRequestError && error.code === UNIQUE_KEY_CONSTRAINT_ERROR_CODE) { console.log('Caught PrismaClientKnownRequestError P2002 - Unique Key Key Constraint, retrying upsert.'); await this.getSessionTable().upsert({ where: { id: session.id }, update: data, create: data, }); return true; } throw error; } return true; } async loadSession(id) { await this.ensureReady(); const row = await this.getSessionTable().findUnique({ where: { id }, }); if (!row) { return undefined; } return this.rowToSession(row); } async deleteSession(id) { await this.ensureReady(); try { await this.getSessionTable().delete({ where: { id } }); } catch { return true; } return true; } async deleteSessions(ids) { await this.ensureReady(); await this.getSessionTable().deleteMany({ where: { id: { in: ids } } }); return true; } async findSessionsByShop(shop) { await this.ensureReady(); const sessions = await this.getSessionTable().findMany({ where: { shop }, take: 25, orderBy: [{ expires: 'desc' }], }); return sessions.map((session) => this.rowToSession(session)); } async isReady() { try { await this.pollForTable(); this.ready = Promise.resolve(true); } catch (_error) { this.ready = Promise.resolve(false); } return this.ready; } async ensureReady() { if (!(await this.ready)) throw new MissingSessionStorageError('Prisma session storage is not ready. Use the `isReady` method to poll for the table.'); } async pollForTable() { for (let i = 0; i < this.connectionRetries; i++) { try { await this.getSessionTable().count(); return; } catch (error) { console.log(`Error obtaining session table: ${error}`); } await sleep(this.connectionRetryIntervalMs); } throw Error(`The table \`${this.tableName}\` does not exist in the current database.`); } sessionToRow(session) { const sessionParams = session.toObject(); return { id: session.id, shop: session.shop, state: session.state, isOnline: session.isOnline, scope: session.scope || null, expires: session.expires || null, accessToken: session.accessToken || '', userId: sessionParams.onlineAccessInfo?.associated_user .id || null, firstName: sessionParams.onlineAccessInfo?.associated_user.first_name || null, lastName: sessionParams.onlineAccessInfo?.associated_user.last_name || null, email: sessionParams.onlineAccessInfo?.associated_user.email || null, accountOwner: sessionParams.onlineAccessInfo?.associated_user.account_owner || false, locale: sessionParams.onlineAccessInfo?.associated_user.locale || null, collaborator: sessionParams.onlineAccessInfo?.associated_user.collaborator || false, emailVerified: sessionParams.onlineAccessInfo?.associated_user.email_verified || false, }; } rowToSession(row) { const sessionParams = { id: row.id, shop: row.shop, state: row.state, isOnline: row.isOnline, userId: String(row.userId), firstName: String(row.firstName), lastName: String(row.lastName), email: String(row.email), locale: String(row.locale), }; if (row.accountOwner !== null) { sessionParams.accountOwner = row.accountOwner; } if (row.collaborator !== null) { sessionParams.collaborator = row.collaborator; } if (row.emailVerified !== null) { sessionParams.emailVerified = row.emailVerified; } if (row.expires) { sessionParams.expires = row.expires.getTime(); } if (row.scope) { sessionParams.scope = row.scope; } if (row.accessToken) { sessionParams.accessToken = row.accessToken; } return shopifyApi.Session.fromPropertyArray(Object.entries(sessionParams), true); } getSessionTable() { return this.prisma[this.tableName]; } } class MissingSessionTableError extends Error { cause; constructor(message, cause) { super(message); this.cause = cause; } } class MissingSessionStorageError extends Error { constructor(message) { super(message); } } async function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } exports.MissingSessionStorageError = MissingSessionStorageError; exports.MissingSessionTableError = MissingSessionTableError; exports.PrismaSessionStorage = PrismaSessionStorage; //# sourceMappingURL=prisma.js.map