UNPKG

pebblebed

Version:

Simplified interactions with Google Datastore for NodeJS

218 lines (183 loc) 7.94 kB
import PebblebedModel from "../PebblebedModel"; import { DatastoreQueryRegular, TReturnOnly } from "../"; import { DatastoreQueryResponse, InternalDatastoreQuery, TFilterComparator } from "../types/PebblebedTypes"; import extractAncestorPaths from "../utility/extractAncestorPaths"; import augmentEntitiesWithIdProperties from "../utility/augmentEntitiesWithIdProperties"; import convertToType from "../utility/convertToType"; import Core, { UNSET_NAMESPACE } from "../Core"; import pickOutEntityFromResults from "../utility/pickOutEntityFromResults"; import { throwError, warn } from "../Messaging"; import deserializeJsonProperties from "../utility/deserializeJsonProperties"; const crypto = require("crypto"); export function createDatastoreQuery<T>(model: PebblebedModel, namespace: string|null): DatastoreQueryRegular<T> { const idProp = model.entityIdProperty; const kind = model.entityKind; const hasIdProp = model.entityHasIdProperty; const type = hasIdProp ? model.entitySchema[model.entityIdProperty!].type : null; const schema = model.entitySchema; const ns: string|null = namespace !== UNSET_NAMESPACE ? namespace : (Core.Instance.namespace !== UNSET_NAMESPACE ? Core.Instance.namespace : null); const dsQuery = (ns != null && ns !== UNSET_NAMESPACE) ? Core.Instance.dsModule.createQuery(ns, model.entityKind) : Core.Instance.dsModule.createQuery(model.entityKind); const runQuery = dsQuery.run.bind(dsQuery); const filterQuery = dsQuery.filter.bind(dsQuery); const useCache = (model.modelOptions.neverCache || !Core.Instance.caching) ? false : Core.Instance.cacheDefaults.onQuery; const returnOnlyEntity: TReturnOnly|null = null; const cachingTimeSeconds = model.modelOptions.defaultCachingSeconds != null ? model.modelOptions.defaultCachingSeconds : Core.Instance.defaultCachingSeconds; return Object.assign( dsQuery, { returnOnlyEntity, useCache, cachingTimeSeconds, enableCache(on: boolean) { this.useCache = on; return this; }, cachingSeconds(seconds: number) { this.cachingTimeSeconds = seconds; return this; }, first() { this.returnOnlyEntity = "FIRST"; return this; }, last() { this.returnOnlyEntity = "LAST"; return this; }, randomOne() { this.returnOnlyEntity = "RANDOM"; return this; }, filter( property: string, comparator: TFilterComparator, value: string | number | boolean | Date ) { if (!schema[property]) { throwError(`Property "${property}" doesn't exist on entity schema for [ ${kind} ]`); } return filterQuery(property, comparator, convertToType(value, schema[property].type)); }, withAncestors(...args: any[]) { const ancestors = extractAncestorPaths(model, ...args); if (ns != null) { this.hasAncestor( Core.Instance.dsModule.key({ namespace: ns, path: [].concat.apply([], ancestors), }) ); } else { this.hasAncestor(Core.Instance.dsModule.key([].concat.apply([], ancestors))); } return this; }, async flushQueryInCache(): Promise<any> { if (Core.Instance.cacheStore != null) { const hash = createHashFromQuery(this); await Core.Instance.cacheStore.flushQueryResponse(hash, this); } else { warn(`Trying to flush a query - but no Cache Store has been set on Pebblebed instance!`); } }, async run(throwIfNotFound: boolean = false) { let hash: string|null = null; if (Core.Instance.cacheStore != null && Core.Instance.cacheStore.cacheOnQuery && this.useCache) { hash = createHashFromQuery(this); const queryResponse: DatastoreQueryResponse<T> = await Core.Instance.cacheStore.getQueryResponse( hash!, this ); if (queryResponse != null) { cachingAugmentQueryEntitiesWithRealKeys(queryResponse); deserializeJsonProperties(queryResponse.entities, schema); if (this.returnOnlyEntity != null) { return pickOutEntityFromResults(queryResponse.entities, this.returnOnlyEntity) as T; } return queryResponse; } } const data = await runQuery(); if (hasIdProp && data[0].length > 0) { augmentEntitiesWithIdProperties(data[0], idProp!, type!, kind); } const queryResponse = { entities: data[0], info: data[1], }; if ( Core.Instance.cacheStore != null && Core.Instance.cacheStore.cacheOnQuery && this.useCache && queryResponse.entities.length > 0 ) { if (hash == null) { hash = createHashFromQuery(this); } cachingAugmentQueryEntitiesWithSerializableKeys(queryResponse); await Core.Instance.cacheStore.setQueryResponse(queryResponse, hash!, this.cachingTimeSeconds, this); removeSerializableKeysFromEntities(queryResponse); } deserializeJsonProperties(queryResponse.entities, schema); if (queryResponse.entities.length === 0 && throwIfNotFound) { console.error(`Couldn't find any ${model.entityKind} entity(s) with specified query:\n\n${createDataStringFromQuery(this)}`); throwError(`Couldn't find any ${model.entityKind} entity(s) with specified query, see server log for more detail`); } if (this.returnOnlyEntity != null) { return pickOutEntityFromResults<T>(queryResponse.entities, this.returnOnlyEntity); } return queryResponse; }, } as Partial<DatastoreQueryRegular<T>> ) as DatastoreQueryRegular<T>; } export function createDataStringFromQuery(query: InternalDatastoreQuery): string { return `namespace:${query.namespace != null ? query.namespace : ""} kinds:${query.kinds.join("-KIND_JOIN-")} filters:${JSON.stringify(query.filters)} limit:${query.limitVal} offset:${query.offsetVal} orders:${query.orders.join("-ORDERS_JOIN-")} select:${query.selectVal.join("-SELECT_JOIN-")} groupBy:${query.groupByVal.join("-GROUP_BY_JOIN-")} start:${query.startVal} end:${query.endVal}`; } export function createHashFromQuery(query: InternalDatastoreQuery) { /*const dataString = `namespace:${query.namespace != null ? query.namespace : ""} kinds:${query.kinds.join("-KIND_JOIN-")} filters:${JSON.stringify(query.filters)} limit:${query.limitVal} offset:${query.offsetVal} orders:${query.orders.join("-ORDERS_JOIN-")} select:${query.selectVal.join("-SELECT_JOIN-")} groupBy:${query.groupByVal.join("-GROUP_BY_JOIN-")} start:${query.startVal} end:${query.endVal}`;*/ return crypto.createHash("sha1").update(createDataStringFromQuery(query)).digest("base64"); } const serializableKeyName = "__pebblebed_serializable_key__"; function cachingAugmentQueryEntitiesWithSerializableKeys(queryResponse: DatastoreQueryResponse<any>) { for (const entity of queryResponse.entities) { entity[serializableKeyName] = entity[Core.Instance.dsModule.KEY]; } } function removeSerializableKeysFromEntities(queryResponse: DatastoreQueryResponse<any>) { for (const entity of queryResponse.entities) { delete entity[serializableKeyName]; } } function cachingAugmentQueryEntitiesWithRealKeys(queryResponse: DatastoreQueryResponse<any>) { for (const entity of queryResponse.entities) { entity[Core.Instance.dsModule.KEY] = entity[serializableKeyName]; delete entity[serializableKeyName]; } }