Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ParserModel not implemented yet (kind:180) #100

Open
helmturner opened this issue Jan 27, 2023 · 7 comments
Open

ParserModel not implemented yet (kind:180) #100

helmturner opened this issue Jan 27, 2023 · 7 comments
Labels
bug Something isn't working

Comments

@helmturner
Copy link

I'm defining a database middleware per the docs as follows:

// src/db/index.ts

/* eslint-disable no-var */
import { type Collection, MongoClient, type Db } from "mongodb";
import { env } from "env";
import type {
  PheroParams,
  PheroContext,
  PheroNextFunction,
} from "@phero/server";
import type { CatalogueItem } from "src/api/products/models";

var cachedClient: MongoClient;
var cachedDb: Db;

export type DbMiddlewareContext = {
  db: Db;
  client: MongoClient;
  collections: {
    products: Collection<CatalogueItem>;
  };
};

export async function databaseMiddleware(
  params: PheroParams, // not used in this middleware
  context: PheroContext, // not used in this middleware
  next: PheroNextFunction<DbMiddlewareContext> // we'll be adding `db` to the context, to be used later on
) {
  const client = cachedClient
    ? cachedClient
    : await MongoClient.connect(env.MONGODB_URI, { appName: "catty" });
  const db = cachedDb ? cachedDb : client.db(env.MONGODB_DB);

  cachedClient = client;
  cachedDb = db;

  await next({
    db,
    client,
    collections: {
      products: db.collection("products"),
    },
  });
}

I add it to my Phero File like so:

// src/phero.ts
import { createService } from "@phero/server";
import * as products from "src/api/products";
import { databaseMiddleware } from "src/db";

export const productService = createService(products, {
  middleware: [databaseMiddleware],
});

I use the exported DbMiddlewareContext type as the type of the first argument in functions which expect to follow this middleware, like so:

// src/api/createProduct.ts
import type { CatalogueItemInput } from "./models";
import type { DbMiddlewareContext } from "src/db";
import { ObjectId } from "mongodb";

export async function createProduct(
  context: DbMiddlewareContext,
  product: CatalogueItemInput
): Promise<CatalogueItemInput & { id: string }> {
  const { collections } = context;
  const { products } = collections;
  const withId = { _id: new ObjectId(), ...product };
  const result = await products.insertOne(withId);
  if (!result.acknowledged) throw "failed";
  return { ...product, id: withId._id.toHexString() };
}

For some reason, I get the following error:

src/db/index.ts:15:6 - error TS-PheroError:
S145: ParserModel not implemented yet: Db (kind:180)
15 db: Db;
If you think this is a bug, please submit an issue here: https://github.com/phero-hq/phero/issues

Have I found a bug or just a limitation / missing feature?

@Jpunt
Copy link
Contributor

Jpunt commented Jan 27, 2023

@helmturner Your code seems great, let me get into it to figure out what’s going on 👍

@Jpunt Jpunt added the bug Something isn't working label Jan 27, 2023
@helmturner
Copy link
Author

Thanks! Forgot to mention I'm on version 0.9.13.

I was able to glean a bit more information by running npx phero server build, which gave me the following output with the error message:

node: <ref *6> NodeObject {
    pos: 362,
    end: 365,
    flags: 0,
    modifierFlagsCache: 0,
    transformFlags: 1,
    parent: <ref *3> NodeObject {
      pos: 356,
      end: 366,
      flags: 0,
      modifierFlagsCache: 536870912,
      transformFlags: 1,
      parent: <ref *1> NodeObject {
        pos: 354,
        end: 454,
        flags: 0,
        modifierFlagsCache: 0,
        transformFlags: 1,
        parent: <ref *2> NodeObject {
          pos: 319,
          end: 455,
          flags: 0,
          modifierFlagsCache: 536870913,
          transformFlags: 1,
          parent: SourceFileObject {
            pos: 0,
            end: 1059,
            flags: 2048,
            modifierFlagsCache: 0,
            transformFlags: 4195713,
            parent: undefined,
            kind: 308,
            statements: [Array],
            endOfFileToken: [TokenObject],
            fileName: '/Users/alec/dev/Catty/1/src/db/index.ts',
            text: '/* eslint-disable no-var */\n' +
              'import { type Collection, MongoClient, type Db } from "mongodb";\n' +
              'import { env } from "env";\n' +
              'import type {\n' +
              '  PheroParams,\n' +
              '  PheroContext,\n' +
              '  PheroNextFunction,\n' +
              '} from "@phero/server";\n' +
              'import type { CatalogueItem } from "@/api/products/models";\n' +
              '\n' +
              'var cachedClient: MongoClient;\n' +
              'var cachedDb: Db;\n' +
              '\n' +
              'export type DbMiddlewareContext = {\n' +
              '  db: Db;\n' +
              '  client: MongoClient;\n' +
              '  collections: {\n' +
              '    products: Collection<CatalogueItem>;\n' +
              '  };\n' +
              '};\n' +
              '\n' +
              'export async function databaseMiddleware(\n' +
              '  params: PheroParams, // not used in this middleware\n' +
              '  context: PheroContext, // not used in this middleware\n' +
              "  next: PheroNextFunction<DbMiddlewareContext> // we'll be adding `db` to the context, to be used later on\n" +
              ') {\n' +
              '  const client = cachedClient\n' +
              '    ? cachedClient\n' +
              '    : await MongoClient.connect(env.MONGODB_URI, { appName: "catty" });\n' +
              '  const db = cachedDb ? cachedDb : client.db(env.MONGODB_DB);\n' +
              '\n' +
              '  cachedClient = client;\n' +
              '  cachedDb = db;\n' +
              '\n' +
              '  await next({\n' +
              '    db,\n' +
              '    client,\n' +
              '    collections: {\n' +
              '      products: db.collection("products"),\n' +
              '    },\n' +
              '  });\n' +
              '}\n',
            languageVersion: 4,
            languageVariant: 0,
            scriptKind: 3,
            isDeclarationFile: false,
            hasNoDefaultLib: false,
            bindDiagnostics: [],
            bindSuggestionDiagnostics: undefined,
            externalModuleIndicator: [NodeObject],
            setExternalModuleIndicator: [Function: callback],
            pragmas: Map(0) {},
            checkJsDirective: undefined,
            referencedFiles: [],
            typeReferenceDirectives: [],
            libReferenceDirectives: [],
            amdDependencies: [],
            commentDirectives: undefined,
            nodeCount: 156,
            identifierCount: 59,
            identifiers: [Map],
            parseDiagnostics: [],
            path: '/users/alec/dev/catty/1/src/db/index.ts',
            resolvedPath: '/users/alec/dev/catty/1/src/db/index.ts',
            originalFileName: '/Users/alec/dev/Catty/1/src/db/index.ts',
            packageJsonLocations: undefined,
            packageJsonScope: undefined,
            imports: [Array],
            moduleAugmentations: [],
            ambientModuleNames: [],
            resolvedModules: [Object],
            symbol: [SymbolObject],
            locals: [Map],
            nextContainer: [NodeObject],
            endFlowNode: [Object],
            symbolCount: 34,
            classifiableNames: [Set],
            lineMap: [Array]
          },
          kind: 262,
          symbol: SymbolObject {
            flags: 524288,
            escapedName: 'DbMiddlewareContext',
            declarations: [Array],
            parent: [SymbolObject],
            isReferenced: 788968,
            id: 26
          },
          localSymbol: SymbolObject {
            flags: 0,
            escapedName: 'DbMiddlewareContext',
            declarations: [Array],
            parent: undefined,
            exportSymbol: [SymbolObject]
          },
          locals: Map(0) {},
          nextContainer: [Circular *1],
          name: IdentifierObject {
            pos: 332,
            end: 352,
            flags: 0,
            modifierFlagsCache: 0,
            transformFlags: 0,
            parent: [Circular *2],
            kind: 79,
            originalKeywordKind: undefined,
            escapedText: 'DbMiddlewareContext',
            flowNode: [Object]
          },
          modifiers: [
            [TokenObject],
            pos: 319,
            end: 327,
            hasTrailingComma: false,
            transformFlags: 0
          ],
          typeParameters: undefined,
          type: [Circular *1],
          illegalDecorators: undefined,
          id: 1320
        },
        kind: 184,
        members: [
          [Circular *3],
          NodeObject {
            pos: 366,
            end: 389,
            flags: 0,
            modifierFlagsCache: 536870912,
            transformFlags: 1,
            parent: [Circular *1],
            kind: 168,
            symbol: [SymbolObject],
            localSymbol: undefined,
            locals: undefined,
            nextContainer: undefined,
            name: [IdentifierObject],
            modifiers: undefined,
            type: [NodeObject],
            questionToken: undefined,
            initializer: undefined
          },
          NodeObject {
            pos: 389,
            end: 452,
            flags: 0,
            modifierFlagsCache: 536870912,
            transformFlags: 1,
            parent: [Circular *1],
            kind: 168,
            symbol: [SymbolObject],
            localSymbol: undefined,
            locals: undefined,
            nextContainer: undefined,
            name: [IdentifierObject],
            modifiers: undefined,
            type: [NodeObject],
            questionToken: undefined,
            initializer: undefined
          },
          pos: 356,
          end: 452,
          hasTrailingComma: false,
          transformFlags: 1
        ],
        symbol: SymbolObject {
          flags: 2048,
          escapedName: '__type',
          declarations: [ [Circular *1] ],
          members: Map(3) {
            'db' => [SymbolObject],
            'client' => [SymbolObject],
            'collections' => [SymbolObject]
          },
          id: 27
        },
        nextContainer: <ref *4> NodeObject {
          pos: 404,
          end: 451,
          flags: 0,
          modifierFlagsCache: 0,
          transformFlags: 1,
          parent: NodeObject {
            pos: 389,
            end: 452,
            flags: 0,
            modifierFlagsCache: 536870912,
            transformFlags: 1,
            parent: [Circular *1],
            kind: 168,
            symbol: [SymbolObject],
            localSymbol: undefined,
            locals: undefined,
            nextContainer: undefined,
            name: [IdentifierObject],
            modifiers: undefined,
            type: [Circular *4],
            questionToken: undefined,
            initializer: undefined
          },
          kind: 184,
          members: [
            [NodeObject],
            pos: 406,
            end: 447,
            hasTrailingComma: false,
            transformFlags: 1
          ],
          symbol: SymbolObject {
            flags: 2048,
            escapedName: '__type',
            declarations: [Array],
            members: [Map]
          }
        },
        id: 1319
      },
      kind: 168,
      symbol: <ref *5> SymbolObject {
        flags: 4,
        escapedName: 'db',
        declarations: [ [Circular *3] ],
        valueDeclaration: [Circular *3],
        parent: SymbolObject {
          flags: 2048,
          escapedName: '__type',
          declarations: [ [NodeObject] ],
          members: Map(3) {
            'db' => [Circular *5],
            'client' => [SymbolObject],
            'collections' => [SymbolObject]
          },
          id: 27
        }
      },
      localSymbol: undefined,
      locals: undefined,
      nextContainer: undefined,
      name: IdentifierObject {
        pos: 356,
        end: 361,
        flags: 0,
        modifierFlagsCache: 0,
        transformFlags: 0,
        parent: [Circular *3],
        kind: 79,
        originalKeywordKind: undefined,
        escapedText: 'db',
        flowNode: { flags: 2 }
      },
      modifiers: undefined,
      type: [Circular *6],
      questionToken: undefined,
      initializer: undefined
    },
    kind: 180,
    typeName: IdentifierObject {
      pos: 362,
      end: 365,
      flags: 0,
      modifierFlagsCache: 0,
      transformFlags: 0,
      parent: [Circular *6],
      kind: 79,
      originalKeywordKind: undefined,
      escapedText: 'Db',
      flowNode: { flags: 2 }
    },
    typeArguments: undefined,
    id: 1321
  }
}

@Jpunt
Copy link
Contributor

Jpunt commented Feb 1, 2023

Thanks for the extra information. The issue seems to have something to do with the fact that we’re validating (runtime) what data flows through all middleware and into your function, and we don’t support everything that is inside of the Mongo API.

I couldn’t get to a nice workaround immediately, but will get to that soon. Could you maybe expose the Mongo client from a singleton, instead of passing it along with middleware? I can see that your approach is how you’d want it, but that could be a workaround for now.

@jim-lub
Copy link
Contributor

jim-lub commented Feb 5, 2023

The reason this doesn't work is because we cannot generate the parsers used for runtime validation (yet?), like @Jpunt said.

You can still use this setup with a workaround by setting the type of the client to unknown in the middleware. You'll have to cast it to the MongoClient and Db type inside your function tho, which is far from ideal but does the job:

// src/db/index.ts
import type {
  PheroParams,
  PheroContext,
  PheroNextFunction,
} from "@phero/server"
import type { CatalogueItem } from "src/api/products/models"

let cachedClient: MongoClient
let cachedDb: Db

export type DbMiddlewareContext = {
  db: unknown
  client: unknown
  collections: {
    products: Collection<CatalogueItem>
  }
}

export async function databaseMiddleware(
  params: PheroParams,
  context: PheroContext,
  next: PheroNextFunction<DbMiddlewareContext>,
) {
  const client = cachedClient
    ? cachedClient
    : await MongoClient.connect(env.MONGODB_URI, { appName: "catty" })
  const db = cachedDb ? cachedDb : client.db(env.MONGODB_DB)

  cachedClient = client
  cachedDb = db

  await next({
    db,
    client,
    collections: {
      products: db.collection("products"),
    },
  })
}
// src/api/createProduct.ts
import type { CatalogueItemInput } from "./models"
import type { DbMiddlewareContext } from "src/db"

export async function createProduct(
  context: DbMiddlewareContext,
  product: CatalogueItemInput,
): Promise<CatalogueItemInput & { id: string }> {
  const db = context.db as Db
  const client = context.client as MongoClient
}

Please note that I just tested this with a Prisma setup and not mongo, but it should be similar.

@helmturner
Copy link
Author

helmturner commented Feb 5, 2023 via email

@Jpunt
Copy link
Contributor

Jpunt commented Feb 6, 2023

my brain keeps trying to spell it phero-phile 🙄

Yeah, we got that too 😅 Any suggestions? Phero-entry/declaration/code/?

@helmturner
Copy link
Author

3 & 1/2 months later... how about "Phero module" or "Phero spec" or "Phero def"? The latter is reminiscent of "Type def", which seems fitting for the nature of the lib.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants