Course Builder Adapter

In the skill stack products, there's a file, a definite candidate for refactoring. This file has a myriad of useful methods you could call from anywhere, effectively making it a kind of skill stack adapter. An interface has been created from which the file extends, within the core source and adapters.

The Skill Products Commerce SDK extends this interface. This SDK includes functions like 'get purchase details,' and 'create merchant purchase.' An extension of the SDK, the Drizzle adapter is where you'll find the current work. Features include content and commerce schemas, primarily drifting over from the skill stack. 'Coupons' for instance have been translated for use with Drizzle.

With the adapters, deciphering how Drizzle interprets decimals proved somewhat tricky. You'll find the merchant accounts among other things here. These all get slurped up into the schema where the adapter is indexed. This schema also includes the Drizzle schema and gets exported. From the database, it is then configured in Drizzle.

While the naming conventions and plurality handling have been a bit tricky to maneuver, the implementation provides a way to work against the interface. In the end, this results in the Drizzle adapter. The adapter ends up extending the interface for the NextAuth adapter and incorporates Auth adapter stuff. To access the NextAuth options, you need to go to the Skill NextAuth Prisma Adapter.

Extra methods specific to Course Builder such as 'get video resource' are also available. For now, several methods are not implemented, pending further testing. Util package holds these actual tests. Any adapter should be capable of running against this set of tests. Test procedures entail passing some fake data and checking if the return matches the expectation. Running Docker facilitates this process.

Over in Course Builder, you have access to the course builder adapter through the ingest functions in core. This abstraction eliminates the need for specific database implementation details. The adaptability of these functions enables it to work with any database backing it as long as it conforms to the adapter interface.

The adapter is accessible via the internal options while actions run. Creating a user entails a similar process. You carry this out via the adapter without any implementation details. This same accessibility applies to functions to perform tasks such as subscribing to a List, getting a user by email, etc. With this setup, the core remains portable, as you separate it from the adapter where the specifics of databases exist.

While some logic would require repetition if creating Postgres and SQLite adapters, it may be more efficient to extract components into reusable structures. But this setup proves quite functional as it stands.


[00:01] In the skill stack in the products we have this file. I don't know why it looks like this, but it probably needs to be built or something. But it has all these methods where you can call it from anywhere. So in a lot of ways this would be like the skill stack adapter. So I made an interface out of all of this and brought that I want to move this down do you and let's see I was doing app stuff down here in packages there's the core source and adapters and can ignore all that and there's the course builder adapter which extends the interface for the next auth or authjs adapter, and then extends the skill products commerce SDK, and then add some stuff in here.

[01:04] So down here Skill Products Commerce SDK is that same thing we're looking at over here, right? Like this Git SDK and that just has, you know, like Git purchase details, blah bidi blah, create merchant purchase, or charge and purchase, which is that big kind of gnarly one. Where did that go? All right, so then in the adapter, like so this is the interface. Core exports the interface that we can work against, and then in the Drizzle adapter, come in here and these are all no-opt.

[01:44] This is Postgres and SQLite, which are just kind of standing here for later, and I've been working on my SQL. And if you come in here you'll see content schemas and then there's these commerce schemas. And commerce is mostly what got brought over from the Skill Stack. So coupons, all those, and this is the same schema set as we have over there, just translated into this because we're using Drizzle. So bringing it over into Drizzle.

[02:17] The only thing I really had problems kind of figuring out is the decimals are a little weird in terms of how Drizzle handles that, but not too bad. So these are all the merchant accounts, all that fun stuff. This all gets kind of slurped up into, well, the index is where the adapter is, but then the schema gets exported. All those little builders, it's using this because we put a prefix on the table name. So that will actually hinder it being like a one-to-one because of the prefix in terms of the existing skill product, because they aren't prefixed with a product name at the table level.

[02:58] But this is the schema, like So you click into that and here's the Drizzle schema. That gets brought up into here. So get course builder schema. And then over here in AppLand, so pro AWS, if we come over here into the database, that schema's here, right? And then it gets configured in Drizzle.

[03:23] And this, like so Drizzle would normally have this and you'd have all of these exploded into here, but we're loading this. And you could technically, you could add more, like if you wanted to have more tables or whatever for a specific product you definitely could. I don't know that we'll ever need that, but that's how this gets. You can see all of the accounts and the relations and then also our commerce stuff, so merchant account, merchant charge, all that stuff which should be, I haven't made any changes and should be basically a one-to-one migration into this. I think it would have to be ultimately be migrated into this, but the shape of it's pretty close.

[04:01] It would be pretty simple, honestly, because the prefix thing is actually something that would pass in, so we could get rid of that. And if in those apps you didn't have this at all, it might just connect and work. I'm not, I haven't tried that yet, so I don't know. Let's see. So back to this.

[04:21] So this is kind of overloaded. This is in packages, adapter, drizzle, source, lib, MySQL, schemas. And the index has that, get the builder thing to build the schema out and this createTables method which then we call and we use the createTables as a type, right? So the default schema is generated from the return type of all these fun things. And then that gives us this ability to call create tables.

[04:55] And I've been having some like naming conflicts with the way Drizzle handles all this and then some annoying things of like some things are plural and some things aren't. We didn't use plurals in our schema, and so we end up with some stuff that goes back and forth. But then I'm just going through and implementing these methods, right? So here's all of the methods and this also includes you come all the way down, this includes the next auth stuff so this is a fork of next auth, so it actually includes, well, our use verification token's a little different because we have this timeout and stuff, but this is like a one-to-one of what we have, and also including the Auth adapter stuff, so all of that gets put in here and it becomes our adapter because it extends the interface for the NextAuth adapter. And we're using ours, our drizzle adapter.

[05:55] And then we do that in products also. There's a, for some place of the, I don't remember where it sits, in like API, I think. Someplace we actually, maybe it's here. I don't know. There it is, default next-auth options.

[06:21] No, oh, here it is, skill next-auth Prisma adapter. So I'd done that and we were doing that, it works just fine. So that's all those methods. And there's a lot of these that, so like get content resource, so these are new ones. These are specific to the course builder.

[06:39] So there's added in, I get video resource, that sort of thing. For working with that, and then all of these, I have a bunch of method not implemented yet. One of the cool bits about this whole thing is that if you come down here into, well you come into test, but then you run basic tests which are in the util package, and it's in the util package down here, utils adapter.ts, and this is actually where the actual tests are run. What's interesting is these are detached from the adapter, and this is a general adapter test. So any adapter should be able to run against this set of tests.

[07:17] And we get down here and there's this stuff that's set up from, because again, I'm just pulling this from NextAuth for the base stuff. But so this is set up to make sure the methods are there. So what are the required methods of any given adapter that we absolutely have to have. But then I'm testing against the adapter too for things like create merchant charge and purchase, which is something from our Prisma API. So then you get this ability to call the adapter like that and test it out, just passing some fake data.

[07:47] This is actually running, it's running against Docker, so I'll just run it. So this creates a database, runs it, that works in CI too, so it's running a Docker database. And so the test pass, and this just works as expected. Get purchase, create purchase returns, a new purchase, et cetera. And the idea being that I'll go through and start, as I migrate these over and what I'm doing is coming into, into the Prisma API and just kind of marching through them and going line by line and migrating it over and then adding a test to kind of exercise that at the end and cover it where that you know this gnarly one where we're creating the merchant charge in the purchase go through and you know it needs probably a series of tests and So make the fixtures and run all the tests across that to make sure it's created all the right things and then feel comfortable that what is getting migrated should function.

[08:58] Let's see. So where these start to be kind of, I guess, interesting is over here and back into CourseBuilder and you get into Core and say you're inside of ingest. What you have in your ingest functions is this ability to access the course builder adapter here from inside of core. So this doesn't have any specific database implementation details in terms of whether it's Prisma or how you get the data, it doesn't matter. We are abstracting that, so we're just getting a video resource.

[09:45] So you lose the power, like you can't do querying or whatever in here, which in practice is actually good, because now these are very portable. And ultimately, it could be whatever database is backing it, as long as it's conforming to that adapter interface. So that's true in ingest. Where else would that be true? And then that's also true If you think about the way that we are executing, so we have the different providers.

[10:23] So those should, I think, have inside of the core, we have the adapters, they're running. You have any of the actions, right? Like so wherever we're handling this sort of thing, We have access to the adapter through our internal options here. So there's always an adapter that we have access to. So when actions are running, you have access to that.

[10:57] And again, you don't, you know, like, so to create a user, it's through the adapter, and you create a user this way and there is no implementation details and you can do you know like whatever it is you want to do similar to the providers where you're subscribing to a Lisp, get user by email etc You are able to do all that without needing to dig in and get into the messy details and make this portable. So everything at core is very portable, separate from the database adapter where you're into very specific database-y kind of stuff. And you can mostly see that work that's done here. One thing for sure is that you now would have to repeat this, right? Like, so this is going to have to be repeated if we wanted to make the Postgres and SQLite adapters.

[11:50] You end up having to repeat this logic and I would suspect that we'd want to extract it into some sort of reusable this you know like these chunky ones anyway could probably be extracted into better structures. But for now, this should work pretty well.