Archive.fm

North Meets South Web Podcast

Tenanted files, testing batch jobs, and enhanced enumerations

In this episode, Jake and Michael discuss potential security considerations around segmenting file uploads per tenant, approaches to testing batch jobs in Laravel, and perhaps the untold power of using enums in PHP.

Show links

Duration:
47m
Broadcast on:
12 Sep 2024
Audio Format:
other

In this episode, Jake and Michael discuss potential security considerations around segmenting file uploads per tenant, approaches to testing batch jobs in Laravel, and perhaps the untold power of using enums in PHP.

Show links

Hey, I am Michael Drenda, and I'm Jake Bennett. Oh, yeah. And this is episode 162 of the North Mid South, where podcast. Check it, check it out. How many times do I have to do this before I learn my lesson? Every time I open a command like that, I get it all over my keyboard. And it's just a mess. So what in the world? This is Swoon PhD. I actually messaged Aaron and said he should try this. I think the way that it works is that you have to send him a case of it. You have to ship it to his office. Are you serious right now? Is that what he said? Like, so in order for him to drink a soda on his, oh, serious. Serious talking to me. So he wants me to send him a case of, no, I don't think it's it. It doesn't want you to. That's what happened. I'm thinking after he put the first episode out and he put, you know, his office address into the internet, people started sending him cases of various sparkling waters. So Aaron, if you're listening to this. If it was only that easy, he doesn't listen to this show. Let's be real. He is a man of the people. Dude, dude barely has enough time to record all the podcasts that he's on. Much less listen to podcasts. Pretty sure. I feel like I feel like he records a lot of podcasts because I just saw like he posted a link to the one that he did with Galba from Terso back in June. And it's the first I'm seeing anything of it. So yeah. I know he's everywhere. The dude is everywhere. I mean, when it when it is your full-time job to create content, I suppose it makes it a bit easier when you're in time nine to five is content creation. Then for the guy who are doing this in our lunch breaks and after the kids are in bed and things like that. No, for sure. I think that's definitely true. I mean, let's be real. I'm stoked for him. I mean, he's done the thing is like it's not like he's a slime bag. And he's just like getting on the shows. I mean, the reason people have him on the show is because he's a genuinely really nice guy. A really good a really good guest because he's great at talking and he's interesting and he's funny. And the quality of the content he puts out is next level. Like, so if you saw that new the postgres trailer thing, mastering postgres. I mean, it looks like a trailer for like a album or something. It's incredible. Like the amount of effort and work that he puts into this. And then his friend Steve as well, right? Try hard studios, both of them together. It's just incredible stuff. And so, you know, good on him. I'm glad it's going well for him. You'd hate to see somebody put in all that effort and then fail. I'm really glad that people are recognizing it for what it is. And, you know, rewarding him, I guess, quote unquote. And I mean, maybe not like monetarily. But like rewarding him with like, you know, distribution, essentially. Getting on the show. Oh, yeah. Getting him in front of other people, you know. Well, I messaged him this morning because I woke up and I'm tired. Twitter timeline was just like his face over and over again. Everyone just like sharing, retweeting, quote, tweeting him reposting. Like everyone saying, you know, how good this this video looks. So I think I watched it about four or five times. I watched it once and then I messaged him. I was like, dude, I think it was fire. It was awesome. So good. So I said, is there any concern? Is there any concern over using Frank's and after it? Like what's what's the worst here? And I think we both decided that the worst would be that they get like a season deceased. And they would just have to tie the video down. But at that point, they've gotten the exposure for it. Yeah. Yeah. Yep. Really good. I mean, the video call it. I mean, like the, I wonder what shot it on. I mean, he had to settle those shots up by himself. It's not like he has an assistant war running around in his office. They, you know, some honestly, they did. Someone, someone that did, he did have someone come out. I think you tweeted about it all the way. Sorry. Okay. And like, we're just, we're just feeding that machine right now. We're just talking about his content creation business. Yeah. You should all check out what is it? Mastering postgres.com or something like that. So. Who knows? I don't have any idea. Yeah. I've never used it. I did like this in my life. But I think, I think between, between this course and Larabell Cloud having serverless postgres by default, although I think Taylor did say in his Q&A that, that they plan to have MySQL available on launch. But I think between those two things, there is a good chance that there is going to be a lot, a lot more Larabell developers in particular looking at postgres in the coming months. Yeah. Sure. Why not? I mean, the only thing is I don't know anything about it. That's the only limiting factor for me. But I mean, if you're using all the, I was going to say, if you're using the grammar without like eloquent, it doesn't really matter. I mean, it's just, you know, it's not just another driver. So who cares? But anyway, yes. No, it's a, I'm glad for him. I've got from it's, it's good stuff. There was something else I was going to talk about regarding that, but don't remember what it was. So no worries. Okay. Couple of things I would love to talk to you about today. Number one, I would love to talk to you about, um, tenancy in Larabell. And talk about storing files per tenant in a segmented location. We'll talk about that. I think that's interesting. And I think it's something it's a challenge we're trying to solve right now. I also want to talk about an IM policy that uses the username of an IM user to segment, um, the permissions, which I really, really like. And I can share in a just log, um, it's something that we've used recently. And I sort of just stumbled across and I was like, I really like that pattern. So I'm going to do that. So we should talk about that. And then you had something recently that you were messing with, um, in, you talked about this and cash money, cash money, cash. Oh, you know what else I want to talk about is, um, testing batches. Batches. Yeah, that looks, that looked nolly. What are you? It is a bit, it is a bit gnarly. Yeah. Yeah. But, uh, no, I think, I think we should talk about that a little bit. And then what did you have? You had something the other day you were talking about. And I was like, Oh, this makes a good sense. I'm talking about enums. Ah, that's it. Enums. Yes. Enums. Exactly that. Yep. I'm going to write these down because otherwise I'm going to forget them. You will forget that. That is what always happens. Yep. So I said first, I said we should talk about tenant, uh, storage, uh, talked later about my policy, uh, segmenting by username of IM user. And then you were talking about enums. And what else was there? Did I say something else or no? Batch testing. That's it. Batch testing. Thank you. Batch testing. We'll see if we get through all that. If we don't, that's okay. We could talk about it some other time. What we're on episode 160. So I'm going to write that down here too. And this is why Caleb just talks about stuff as soon as it comes to his brain and notes on work, which I got to say also, if you have not listened to that, you definitely should because it's really awesome. Sorry. Tell me about. Tenanted storage. Tenanted storage. Okay. So here's the challenge, right? I, and maybe it's unnecessary, but my fear is that if I'm using something like Spassie Media Library and I have models that are scoped to a tenant, it feels weird to me to just use the media library to just add on a media item to that tenant. And then it just stores it in this S3 media location. That's it. You have the media disk and everything's just chucked into the root directory. And I guess I just feel like I want a little bit more separation between the files than that, than a record and a database. Like I want to protect myself somehow from my own stupidity. And, you know what I mean? Accidentally giving someone else access to something that doesn't belong to them or them being able to be nefarious and get to a URL that they should not be able to get to or to generate a, you know, temporary link to a file that they should otherwise not have access to. Yeah. And that's so that's my fear. And maybe that's unfounded, but I don't think it is. I think it's actually probably a good idea to protect myself from those sorts of things where like I might accidentally forget or have something like that. So it's just this layered approach, this idea of layered sort of, you know, things that prevent me from having unauthorized access to these different file locations. So what are your thoughts on that first before I dig into it? Like, yay, nay. Do you think that seems like a reasonable fear? Is that what do you think? I think, I think the main thing there would be like collisions and file names between tenants. But if you're using Laravel's default stuff where it generates a file name from a hash or, you know, random string or whatever it is that it does, then I think that's not a concern. I think the default is to like store those things as private files. So you can't, like you can't get a directory listing on S3 and you can't just access these things without assigned URL. So I think the opportunity to accidentally stumble upon a file is taken care of at this, you know, at this high level by S3. I think if you are accidentally leaking media records between tenants, the problem is in like application logic land where you are like, if you are leaking media models between tenants, you are probably potentially leaking other information between tenants. And so that's something that you need to look at that said segmenting, which I agree and that's what I'm saying is like, I am confident that we will get it right most of the time, but I'm afraid that there will be times or there will be something where I will, you know, somebody, if it's not me, somebody on my team will accidentally write something and we want to realize that it is leaking something. And in the case that it does, I want to have another mechanism that will protect me from giving that person access. Yeah. So I mean, if you, if you leak the model to, to the wrong user in the wrong tenant, you are going to leak access to that file anyway, because I assume given that you are using signed URLs to access those files, if you happen to return that model, you are returning a authorized link to that file anyway, irrespective of where it's stored. And this is the backstop, right? So the backstop is if I'm storing them, so let me just tell you sort of this leads into my second part here, which is this idea of a policy in AWS that allows me control access to particular folders, I guess you call it, particular directories inside of a bucket based on the user that is currently authenticating to that bucket. So the way that I have this, this policy set up is it says, allow listing of buckets for, we'll just, let's just call it Jake's media bucket. Okay, so you can list buckets for, for Jake's media bucket, because you have to have that permission for some reason in order to be able to do anything in there, you have to have the ability to list buckets, great. But the ability to get put or delete is restricted to a path that is scoped to the username of the IM user that is currently having this policy applied to them. So if I have a user, an IM user called, let's just use Wilbur, like Wilbur is the name of my employer, that's the tenant is Wilbur. Then what happens is Wilbur has access to Jake's media bucket slash Wilbur slash star. Anything within that directory works. It'll just work, I can get, I can get put and delete in there, which is great. So if there's another one, what's the name of your employer? LMG. LMG. So if we also have Jake's media bucket slash LMG, even in the case that I accidentally get access to a record that belongs to LMG, if I try and go get something that lives in the LMG bucket, because I'm authenticated as the Wilbur tenant, as far as the credentials to AWS are concerned, I will not be able to get assigned URL to LMG. Okay. So that is the backstop. It's like, yes, I may have a, I may have access to the model somehow, but the signed URL, when I go to get that, will be covered up, will be protected by the fact that I have different AWS credentials based on the tenant. Now that brings in other challenges, right? There are other challenges like, now where do you store those AWS credentials and those sorts of things? But in theory, without thinking about those other things, that is my question. It's like, is that a good idea? Does that sound a reason to what I think it does? But I'd like to hear what your thoughts are. Yeah. So we, we at the moment are using the tenancy for Laravel package that kind of handles stuff on the flight. So based on whatever your tenant is, it will swap out the storage at, as it's part of the application boot process. So you go to the scaffolds and sets of prefix to like whatever, the tenant ID. So everything's in one bucket, and then it's /tenant, whatever the ID is, whatever the ID. So all of that stuff is segmented, and we use domain based tenancy. So, you know, it would be Wilbur dot whatever, LMG dot whatever. And so it would then switch to that tenant, you would authenticate to that tenant, and then, but, but it's still all using the same set of credentials to access each of those tenants. So even though the files themselves are segmented, you still have. So I think you, with your IAM policy, you have an additional layer on top. So long as you're also switching the credentials as part of that boot process there, which I think, I think that adds a little bit of extra protection. I think the way that the tenancy for Laravel package does it, it's just swapping the bucket and that, I think is more around a portability perspective, where if you wanted to pick up a tenant and shove them onto their own EC2, for example, onto their own node, you can do that and just pick up that whole thing and bring all their storage and then put it somewhere else, as opposed to like isolating between the buckets. So I think you've got the extra layer at the moment where, you know, you are, you are accounting for the possibility of inadvertently leaking the models, but because you are also switching the credentials used to access those files, even if you did get a URL, it would still deny your access anyway, because it would be generating a link to a file that like with a different signing key. So I think, I think, yeah, I think, I think that's probably fine. And like, it obviously depends on what you're trying to protect here. Like, if it's just profile photos, then it's probably not as critical. But I assume, given the nature of the business that you are protecting financial information, things like that, where you would, yeah, you would want to be, you know, protecting that stuff at a higher level. So yeah. Yeah, personally identifiable information is the biggest, you know, the biggest thing. You don't want to leak that one. Yep. Yeah. And so, and I'm also quite sure that, you know, the level of clientele that we're going to be servicing, they're going to want to know that in addition to logical separation of those files, there's also going to be, you know, some sort of separation of credentials as well, for this exact reason, for the reason we're talking about. Now, the thing is, I haven't exactly figured out how I'm going to do this yet, is the only trick. Like, I don't know the actual architecture I'm going to use to accomplish this. So one way I've thought about doing it is, and the interesting thing about this tenant app that we have is like the max number of tenants will ever have is like 15, right? Like it's, it's not like each tenant can get set up manually. Like I could store each one of the credentials in the ENV if I wanted to. You know what I'm saying? Like could swap them out, but based on the tenant, just on using like some convention of the naming of the ENV and be done with it. So it's like, what I could do is I could use a different, I could even set up in the file systems.php a different disk per tenant that the tenant could have their own disk. And then I would use the credentials. I could store the credentials in there and then I'd swap out the disk and then be, be good to go. So, so that's, that's an option as well. And that would probably happen during the service provider, you know, booting up the app, then you'd swap out the disk and then you do all that stuff. Yeah. I think, I think that's probably an easier way to do it. If you were to configure a disk rather than dynamically switching the credentials of a CPU disk, if you were to, like if you know that it's only going to be 15 or 20 or whatever, that you can just configure them as you go. It's going to be easier to just configure the disk and then target a specific disk based on the tenant rather than trying to swap the credentials used for a single disk at runtime. Right. Right. Yes, exactly. And then the hope there would be, the hope there would be that, you know, you wouldn't make the stupid mistake of saying like, okay, well, when you're trying to go get the documents for this claim, just use the team ID of that claim and then swap to that disk, it's like, no, no, no. The only time you ever swap the disk is at this time you boot the app and that's based on the currently logged in tenant. That's it. And no other places do you ever do that? You always reference the disk that's loaded into the existing, you know, the tenant, the users it's logged in. Never do it based on the actual claim itself. So, yeah, I think that that'd be the only way. So, but I think I would sleep better at night being knowing that that was the case. Yeah, that there's no way for them to swap that out or if I did leak a claim, it's not going to like, you know, it would just basically be like, the claim's not there. It doesn't, I don't see any folder by that name. So, yeah, I think that's reasonable. Okay, cool. That's helpful. I appreciate having that discussion. So, again, with that sort of like, I am policy thing, just as a heads up, like, I can throw that in a just log if that's helpful for anybody. The really big benefit to me was that I didn't have to write a new JSON policy every time I wanted to make a new user that had access to a specific location inside that bucket. All I had to do is create a new user, attach that policy and I was done. Attach one policy. And so, all you have to do there is basically reference the username and the path by just using, I think it's actually a dollar sign. I think it's a dollar username inside of the policy itself and it will use the username of the of the logged in user. Then one other thing to notice there is as well, it's case sensitive. So, like, if you have Wilbur uppercase, the capital W or Wilbur lowercase, those are two different locations or two different. Yeah, yeah, the name of the user is case sensitive and so it matters when you're trying to look at that disk location. So, yeah, there you have it. There you have it. Okay. Yeah, I think, I think you're covering your bases there, given what you're storing, I think the only decision really you need to make is do you want to configure, right, in number of disks or do you want to figure out how to dynamically switch them at runtime? I think I think having a number of desks is probably fine. The only the only thing that I will have to be careful about or just consider is that when I'm doing, like, the media stuff, Spassy Media Library, I think there is a way, though, you could just say on disk, you could just put it on a specific disk. Like, it's a fluent, it's a fluent call. Yeah, I can just do that. No problem. That should be easy enough. And I don't know where I saw this recently. I think it might be like Stephen Bauman has been tweeting these kinds of things recently where he's took and maybe it was you that retweeted it, talking about putting Q names into an enum. And I think you could do the same thing with your tenant, like with your file system disks, just and that way, we do that right now with any of our desks as we put them in. Now, okay, this is actually an interesting one too. Let me, let me cover what you're talking about first, which is, yes, in file systems.php, instead of having a string name for the disk, we use a enum there. Now, we were using just classes and constants, right? Because with a class and a constant, it always references the string value there is you don't have to do arrow value, which looks so much nicer. You could just say disk double colon Wilbur or disk double colon LMG or whatever you're said you said you're supposed. Whereas now I have to do disk double colon Wilbur arrow value, which I did not like that. That's sort of sucks. But you have to have the string representation of it. You can't do anything else. So, which there was a better way to do that, but there isn't. So there is an enums in that way. Samuel who wrote Tennessee for Laravel also has this package for enum augmentations, I guess you want to call them. And one of the features of this package, one of the traits that it includes is the ability to make enums invocable. So you can, you can do, you know, file system disk colon colon Wilbur and then call it as a static function. And it will return the string value. So that way you get, you know, the ID support, you get the completion, and you also get, you know, the ability to get the value of that enum member without having to then chain on arrow value. So that, that feels pretty good. But yeah, it is, it is kind of annoying that PHP does not. Casting it to a string in, in a, you know, in an array context like that. Yeah. Yeah. Yeah. It's annoying. But whatever. So we've just swallowed hard and just been like, okay, we're just going to make it, we're just going to do it that way, arrow value, it is. And so whatever we deal with it. But speaking of enums, tell me about your enum thoughts here. Like you said, you were, yeah, you know, adventure. So it's a little bit about maybe, like, making a little video course or something. Or what are you just like a little mini thing? I don't, I don't know exactly what it looks like. But the, the kids had a sleepover at the grandparent's place on the weekend. So I had a little bit of time, which I spent, of course, most of that time getting my set up working for the recording video. But I just, I just put together like a little two, three minute off the cuff video talking about, you know, enums. And we, we, we all have them now in PHP since version 8.1. And we love to use them. And we love talking about them. And I think there's some interesting opportunities to explore like usage of them beyond just like here is a thing that has like, it's either a backed enum that returns a string value, or it's just a basic enumeration that just has case names. But there's, there's a lot of different ways of using it. Like, PHP doesn't provide a nice way to return a list of options, or like it gives you cases, but it doesn't allow you to target the values or the keys and things like that. So there's ways around that you got to kind of build them all in with your own traits. Yeah. So, um, yeah, there's, there's no, just, just to kind of talk a little bit through that, and, and what that looks like, some options, you know, this, this enum package that I mentioned is a good way to kind of bring that in, in a way that you can use it across projects and across enums really easily without having to, you know, manage your own traits and things like that. But then also talking about more interesting use cases, like providing stateful functionality, and to use like your invoice status. And example from your, yeah, like on talk last year, you know, if you have a status of, you know, draft and active and paid and debt, for example, like you can attach descriptions to those and use them to label things in the, in the UI of your application based on sure, yeah, the current state of that enum. So, you know, talking a little bit about that, talking a little bit about, um, you know, factory instantiation of enums and things like that. So, there's, there's some, like, it's not, it's not like a huge service area, which I thought it would be a good idea to do it because it's like fairly limited in scope and you can kind of go, this is everything in quotes that, that you can do and it's just done. Um, but I think this is interesting exploration that, you know, you see bits and pieces every now and then, but not, um, a great deal of discussion around that kind of stuff. So I thought, you know, just as a little bit of fun, of course, since, since that three minute video, I've had absolutely zero time to work on it. Right. Right. I mean, the kids are, I mean, I can't win the kids are asleep because one of them sleeps, you know, obviously that wall as we've discussed before. So I can't be in here recording videos at night. So yeah, yeah, that's, I get it, dude. I'm, I'm there with you. Like, I have to, I've been, I mean, it feels like we're in an eternal house project mode. You know what I mean? Like it's just never ends. Um, so I'm putting up casing and baseboards right now, but yeah, I can't do it after the kitchen bed because I don't want to wake the kids up. So it's like, you know, you only have so much time. So today all the kids were at a soccer game. So I came home from work and just got got to going on it because it was like, I'm not going to bother anybody if I'm making lots of noise. And so tore it up, but that's how it is. Yeah. Um, no, I think that's, I'm curious how many people in the PHP ecosystem are not using enums. I mean, I guess a prerequisite is you have to be on PHP eight. I put one. Yeah. We put one. And, um, so I mean, you know, depending on what percentage of people are on eight dot one or above, that's going to be, you know, that, but then of the people who are on eight dot one and above, who's not using enums? Um, you know, I don't know. It's like one of those features. It's one of them. I think you said it the other day, you're like, man, it's one of my favorite features of PHP now. He's, he's everywhere, you know, and any place that I see a magic string, I'm like, nope, no, no, no, don't do that. Please don't do that. Please use. We have, we have this enum called yes, no. Oh, I like that. It has two members. Yes. And no, but we, because for historical reasons, a lot of our database actually stores yes and no in fields as opposed to one or zero. Yeah. So in order for us to like operate in a sane way in our code with bullions and then convert them, we've got like this factory method on the yes, no enum called yes, no from bullying and value. And that way we can handle situations like you pass in like some bullion and then like, if it's true, we return yes. And if it's false, we return no, like just that, those small little things, but you call that behavior in one place, which means you can test that like you can throw different things at it. You can decide whether or not you want to handle null, like is null an invalid state? Does null return null from this enum or does null default to false, you know, all those kinds of different things that you co-locate and that behavior is there. So you can do it anywhere that you need to, you know, anywhere we need to do that transformation from a bully into a string of yes or no, we can do it all in that enum, sorry. And vice versa, right? Because it from the database, you can cast to an enum. So you cast from the enum to the yes, no type or sorry. Well, yeah, you cast from the database to the yes, no, enum, and then you have all that behavior again. So like, when you're referencing that value, that column, just, you know, you're dealing with the enum. So that's super handy, man. I mean, all that behavior you get. And then plus just the confidence to know that I'm dealing with one of these values, you know, and you know, it's just, you know, when you're doing a match inside, like in the context of an enum, you never have to put a default state in there, really, because you can only have an enum that exists in a valid, representable state. So, yeah, and there's all this kind of documentation talks about this in the notion of like the enum exists to to prevent you from having a value that is in an unrepresentable state or something like that. Like if the enum is instantiated, then the enum is it contains a valid state. It can't be anything other than what is defined in the enum. You can't have an enum with a value of null. So yeah. Yeah, the only case in which you'd have to have a default state and I've run into this a couple of times is so somebody will have a method called get options or get labels or get colors, right? And so what they do is they start listing all the enum cases in the left and then listing all the values that they should be associated to on the right hand side, which is great, except for when you add a new case and you forget to add it into that method. And now you have a default state, because you can have a valid enum value that does not have a state. And so instead would have tried to do is do something like and so this is a case for attributes, PHP attributes. And so what I will do is I will instead of having and this gets a little bit messy. It's not the it's not the right solution for everybody. But instead of having a get labels. And I'm sorry, I will still have a get labels. But what I will do is on the case itself, I will put an attribute of label on it and I will define the label right above that case. So if I'm adding a new case, all I have to do is follow the convention and add that new label on that new case. And now my get labels says give me all the cases and loop over all of them and give me the associated label. And if there's ever one that doesn't have a label, it throws an error. And so I do the same thing for colors, I did the same thing for whatever. So it's like all of that stuff is right there located on the case itself. Now, like I said, does get a little bit messy sometimes if you have too many things you're trying to do. But it actually works pretty dang well, I got to say. So that's another thing you could add to your enum talk is using attributes to co locate that stuff right up next to the case rather than in a match function. Yeah, I like it. I could send you an example of that too. Because it is a little bit tricky with the reflection, you know, I mean, you have to use that, you know, use the attribute and then the reflection stuff to get the option or they get the label. Yes, that is, that is like, I actually looked into like how to put your own attributes together. And because it is all meta programming, the only way to reference the attributes is to use the reflection API to do things to look for specific members of of a class. And then you've got to look to see like is there an attribute attached and all this other stuff. I guess it's nice if you do it in such a way that it's reusable and you can just put it anywhere. But it's also like this. Yeah, that's what it is. And so like, you know, what I do is I have an attribute call or a trait called has labels. And then has labels has that method on it. And then I already know the attribute is going to be labeled. And then it just, you know, I don't have to write it once. And now I know I have a labels function on there that automatically uses reflection and the label attribute to get the labels. So it's not difficult to implement. And you know, the trait says exactly what's on the, you know, does exactly was printed on the tin, as you guys say. Yeah, I'll send you that. And I'll send anybody else interested in it too. There actually was a layer of a news article that I use to sort of get me on this path. The layer of a news article was like adding your own attributes to your enums or something. That was a while back. Yeah. Nice. Go give credit to the author. Definitely definitely do that. Yeah, I will reference that as well. That's interesting. Hmm. Very good. Awesome. Okay. One of the thing where we got here, we got, we got the testing of batches. Yeah. So you, uh, you were doing this look, um, kind of grim. And I think the realization that we came to in that chat was that yes, it is grim, but there's not really a better way of doing it. The only, yeah, no, there is no better way of doing it currently. So let me explain the situation. So I've got a action that is dispatching a batch. So the action creates a claim. And then I have a batch that gets dispatched that, or sorry, yeah, I have a batch that has two jobs added to it. Just like add notes cover page and add claim cover page. Those are the two jobs. And then I dispatch it. And then I have a then, which tells the batch when you are completed successfully, do this action, right? And then you have a catch, which says, if anything fails, you should do this, right? So that's the situation bus. Jobs. Here's the jobs dispatch, then catch, right? Those are, those are your sort of methods you have there. So, you know, what I want to do is I want to assert in my test that there were two jobs dispatched. And the two jobs that were dispatched were these two jobs. Now, some people would call that like spell check testing. And I would agree, it is spell check testing. But what I'm trying to do is I'm trying to prevent future developer, not me or me from accidentally commenting one of those out or having something stupid happen. You know, some merge conflict comes in and they just delete it. And now my test is, you know, if I don't have this sort of spell check test in place, it's like, yep, everything's fine. So it does. I'm trying to avoid, right? I'm not testing the actual job, the ad claim cover page or add notes cover page, I'm testing the job, I'm testing that job separately. But I want to know that the batch is getting those specific jobs. Okay, so the way that you can do it right now is in your test, you batch fake. And then you batch assert dispatched. And then within there, you get a pending batch. So that pending batch contains the jobs that are added to the batch, excuse you pending pending job arrow jobs, that will give you that. And so then you can do assertions against that. Now it just gives you a collection of them, right? Just gives you a collection of jobs. And so in order to assert, you could say count, count that there's two, and that works fine. Yeah, you can cut those two. But then if you want to assert that the two jobs that you are expecting to be there are there, you kind of have to like map to the get class of the job, you know, you kind of map over each of them, you know, return the class name of the job, and then check to see if it contains the two jobs that you're expecting. That's that's sort of crappy. It doesn't feel good at all. It feels like it should be something like pending batch arrow has job. My job pending batch has, you know, has arrow has job. My job. And then it should, I would want to do something like pending batch, then, you know, whatever, did my then work, you know, I shouldn't even actually have to do then. If I wanted to call then I could do so. Yeah, I feel like number one, I like these kind of smoke tests for events, for jobs, for the things like that, where you just want to fake that this happened, right? You don't, you don't care the side effects of those jobs running or those events firing or whatever else, you just want to make sure that given you hit some controller or something happens in your application, this expected behavior was fired, like these, right? And then, as you say, testing the actual behavior of one or more of those individual jobs is, you know, is the concern of instantiating that job. Yeah, like a test somewhere else that does that. So I feel like JMac has talked about this before about like testing batch jobs or something like that. I'm maybe he's worked on it. Like, I'm sure I've heard about it in passing somewhere. And I feel like it was JMac, but I could be wrong that it was him around, you know, a better way of doing this stuff. Yeah. So I mean, the what I what I ended up seeing and maybe throwing out as a suggestion, and if anybody feels inclined to do so, you should definitely do this. I think it's a great pull request, honestly, to the framework. We have this idea of a suitable JSON inside of our JSON tests, which is awesome. It's really cool. So you can say a certain JSON, and then you have a closure, which has an assertable JSON. And so what that allows you to do is allows you to make all sorts of assertions against the JSON that's in there. You get this really nice class that has all these little helper methods to give you the ability to test all sorts of things in your JSON. The payload that's coming back super cool. So my suggestion would be here when you say bus a certain dispatched, instead of just getting the pending batch, what you would get is something like an assertable pending batch. And then it would that that assertable pending batch would have little helpers on it that would allow you to do exactly what we talked about is this job in the list of jobs that's getting dispatched. You know, does my then closure when it's executed have the existing have the side effects I would expect? That sort of stuff. And so this was an exam that was some a suggestion that somebody gave on Laravel, sorry, on Lara cast forums was they were like, well, when I do my, when I do my assert batched, I take that pending batch and I knew up a fake batch that I have passing in the pending batch. And then they just do exactly what I talked about. They have created a little helper methods to do exactly that test to see if the job is in there, etc. So it's actually not that difficult of a PR to make to the framework. But it would be a pretty cool PR to make. So if anyone wants to scoop me on that one, go for it, because I'm probably not going to do it. But I think it's I think it could be really cool. And yeah, David Hemphill said he talked to Taylor about it. And Taylor was like, yeah, there's no better way to do it right now. And so not saying that there's not a better way it could be done just that currently in the framework, there is no better way to do it. So if you're looking for your first pull request of the framework, go for it. You're pretty good. Well, it would be a good one. Excellent. I don't think I've got anything else. Okay, I think this is a pretty valuable episode. I'm talking about some really good stuff in here. Yes. Thanks for the hot tips around the. Because that gives me some ideas around progression from like, this is what we used to do. pre PHP 0.1 and enums. This is how you might do it using like just an inline method. And then the attribute stuff is nice, because it gives you the ability to kind of do that in a consistent way across all of your different enums without having to like implement the same match thing everywhere. It's just, it's just going to be available. Correct. Yeah, and it protects you against it. It protects you against that stupid thing that I talked about, which is like somebody adds a case and forgets to add to the method, which has happened to be before. That's the reason I know it's a danger is because it actually has happened. And so what you end up having to do is have this default method that just throws an exception in the case that it's something other. And that's annoying too. That doesn't feel good either. And so the, I guess that I'll send you a code snippet of what I have. And what, you know, the nice thing about it that redeeming quality is if there is ever a case that does not have a label on it, it throws that exception saying, well, you have a case that doesn't have a label. So you can't have it get labels attribute or whatever. Yeah, it's always funny testing like that kind of enum behavior, because you find just so duplicating all of that stuff just to make sure that like, yeah, I have set and then okay, I ran the test and it failed because I forgot to do it so you can go back and do it. But it's just like, now I've got a copy and paste the description from the enum into the test. And then, you know, if one changes, the other one has to change. And that kind of, you know, leads you down the path of flaky testing. So yeah, I like, I like this idea. I will send it to you, my friend. And maybe I'll throw it in a gist. And I will send it to the wider world as well, for anybody who cares to do so. And I will find that level news article as well and send it to you. Yeah, I found I found that. Sweet. Yeah, that's a good one. I mean, that's literally, I mean, I don't need to share mine. I mean, that's exactly what I did. What he has there is exactly. Rob funs like a. Yep. Yep. So credit to Rob. Thanks, Rob. It's a good, good call. Cool. Any other thing I have to say is we crossed into, we only have 47, I think, early bow tickets left to Larricon for this year. So I think we're about eight weeks away. So we are doing well. We're having lots of stuff. We're getting into the, like, final stages of, you know, my side of the things now, you know, making sure that the speakers are all set, starting to finalize all of our, or the, like, Taylor setting us up the bomb over here with just the level of production for Larricon US this year. So we've got some ideas. We've got some things that we're going to do. But we're looking forward to it. We just started storyboarding our, I say we, nuclear started storyboarding and scripting the intro video for this year. So, you know, we did it last year and obviously we have to follow up this year. But we've got some cool ideas for that as well that we'll pursue and some other fun stuff. So I'm looking forward to it. It'll be here before we not. You having to put a lot of work into, like, your intro talk or anything like that? Oh, boy. I'm talking about it. I may have made a terrible mistake. Oh, no, I was doing that last year. Yeah. Well, because last year there had been a four-year gap since the last time we just, I thought it was, you know, it was a good idea to kind of set the tone and set the scene for, you know, why we do what we do. Yeah. I just don't know like, how do you do that every year? No, it's hard to do it every year. Yeah. Yeah. I don't think you need to do it every year. No, I don't think so either. So I will, I've got some things written down. Like I've got a document and like I will do some kind of opening address. But I don't think I'm going to tell a story like I did last year. I mean, honestly, again, let's go back to the Aaron Francis, whatever, blah, blah, blah. Sorry, Aaron. We should be giving you royalties or something. But to intro Laravel or Larricon this year, he literally just, I mean, it was honestly like a tiny little stand-up routine. It took like two minutes, right? It was, it was funny. It was, it was very chill. Like, but you know, the thing is too, is it's like, you don't want to put a ton of time into it either because I mean, let me see. How do I preface that? You want it to be good, but like also half the people in the room are just getting seeded. Like when you kick it, actually kick it off. It's just like, you don't want to like stake all this time on it and then be like, be really frustrated if half the people weren't listening because they were just stopping their chat they were having with somebody who haven't seen in a year. So anyway. Yeah, I think, I think this year, it's going to be much more high level. Just like, you know, the general, like, how many, how many of you here for your first time, you know, how many here for your fourth time? Oh, totally. Yeah. Just like that kind of stuff, you know, and just set the time, you know, set the rules and things like that around expectations. And then it just launched straight into it this year, I think. And what we all realized this year at Larricon US is people are really just there to see the people anyway. I mean, like talks are great, you know what I mean? But like, it was so heavy, emphasized, like just, there was like a whole back room that was okay, people who want to hang out, they just want to watch it on a screen, just go back there and chat. And a lot of people did. So it was cool. And so yeah, same feel, right? Don't put too much pressure on yourself. Like, the fact the thing that you're providing is the space for people to meet. That's, that's what you're providing. And you're going to do that regardless. So you've already won, you know? Yeah. Sweet. All right, dude. All right. episode 166. 162. 162. Find show notes for this episode at Northmeats.org/162. Head us up on the Twitter's or the X at Michael Dorinda at Jacob Bennett or Northstar audio. Raise up on your pod catcher of choice. Five stars would be amazing. And if you are looking for custom keyboard keys, you should check out dapkeys.com. I have, I sent another, I sent another picture today. Let me say it's dapkeys. It is dapkeys. I think, I think, I think, I think, I think, I think. Anyway, this dude, Austin Cameron, what's that? The campfire, I would say. Yeah. Austin Cameron is this dude's name, Mediment Larecon. But he gave me a little live wire keycap and a little campfire keycap. Holy crap. Both of them are so cool. They're like in acrylic. They're awesome. I, I can't wait until he opens up his site stuff because I'm going to be buying some of them. And I told him, I was like, yeah, I've got a low profile keyboard. And so he was like, oh, yeah, maybe we can make a different mold for that one or blah, blah, blah. So he's going to, I think he's going to try and do maybe like not a full size keycap, but not a low profile keycap either, because he can't really get too much stuff into the acrylic if you do a super low profile. So he's like going to do like a medium height when I'm like super awesome. So I couldn't be more excited. I'm so geeked out about these keycaps. They look so awesome on my keyboard. I'll have to share. Yeah, they look so fun. So anyway, shout out to Austin. Thanks, dude. All right, everybody. Until next time. Two weeks. We'll see you then. Peace. [MUSIC] [MUSIC]