Having build a very powerful snake, Kevin and Andrew seek to bask in the glory of their intellect by visualizing their wins. But not is all as it seems...
Speaker 0: Hello? Oh, were were you waiting to do the thing? Alright. We can
Speaker 1: I got you excited? I got you excited.
Speaker 0: Hello, and welcome to ready Ready.
Speaker 1: Set. Battlesnake.
Speaker 0: We are running out of time to nail it, and I I have to be honest. My my, my confidence is starting to wane that we're gonna get it. But hello, everyone. Welcome to episode 3 of 4 for Ready, Set, Battlesnake. My name is Kevin.
I work at Directus and
Speaker 1: My name is Andrew, and I work at Devcycle.
Speaker 0: Absolutely. And today, we're gonna pick up right where we left off in building a Battlesnake with, Directus and in the next episode with Devcycle. We've both been involved in the Battlesnake project for some time in various ilks. And honestly, we're we're just having a great time, if I may if I may talk for us both.
Speaker 1: I'm with you. I think this has just been so fun. I haven't dug into Battlesnake in such a long time that this was such an awesome thing to come back into, especially oh, look at that. My light turned off. Especially after, especially after just getting into the the nitty gritty of Directus over the past, like, couple months, getting ready to build some of this stuff out.
So, yeah, this has just been it's been so much fun, and I'm excited that we're only halfway through right now. Like, we've got 2 full sessions left, and I think it's
Speaker 0: gonna be amazing. So am I. So let's recap where we were at just at a high level. So we have a local directors instance running. Inside of that instance, we have a custom endpoint extension.
We have a custom endpoint extension because we need to know with confidence that, a snake can exist having a root endpoint and a slash move endpoint at the very least. And the webhook, the webhook triggers for flows can't guarantee that you get a random URL and we need we need both of those relative past. So we created an endpoint and all the endpoint does is it fires off this flow. There's one for the info of our snake here, which gives the information about our snake. Is the font size okay?
I can bump it up if necessary.
Speaker 1: It's okay. I mean, bump it up a little bit. I feel like it's not bad for folks that are watching. Yeah. Yeah.
It's good.
Speaker 0: Yeah. Yeah. Yeah. Yeah. And then our move endpoint, which is what we spent most of our time working on in the last episode.
So request comes in with a full board state. We set up some global information from that state. We grab is move safe, up, down, left, right, or true. We grab snakes, board width, board height, my head, and you kind of all of these conveniences. Then we run avoid walls, and we see whether whether the wall's gonna be hit.
But what you'll notice is at the top is that we pull in is move safe. We do all the work to it and then all we do is return is move safe. So we're moving this object down the chain kind of manipulating it at each step because that's ultimately all we need. We avoid the walls. We avoid all bodies of all snakes once again, starting with is move safe, ending with is move safe.
Then we avoid head to head. This is what we spent most of last session doing once we got the flow running. It might not be the most elegant, but what we do is we look one space around every snake that isn't us, and we avoid those spaces too. Once again, we start with this move safe coming in, and we end with this move safe going out. And finally I love we determine
Speaker 1: I love that you're saying this is not elegant, Kevin. Like, this is like yours is so much more elegant than mine. As you're gonna find out next week when I jump back in, week 1 week 4 are Andrew, learn learn with Andrew. Specials.
Speaker 0: Andrew specials.
Speaker 1: Exactly. So this is such clean code. I love it.
Speaker 0: Oh, well, thank you very much. I'm sure some of our viewers will disagree and I won't stop them. And then and then finally, we grab that array of is move safe. We basically just pick 1 at random that is still available and we return that as an object move next move, which is either up, down, left or right, and we continue on. And so in this episode, I think we were going to start playing around with not necessarily adding more logic to the snake.
I think it's a pretty robust snake right now, but instead, we're gonna start saving game data to this director's project, and then perhaps we'll do something with the insights module. How does that sound?
Speaker 1: That sounds fantastic, Kevin. That sounds like a great way to spend an afternoon for me slash Wonderful.
Speaker 0: And afternoon, no?
Speaker 1: It is now but I realized that people are watching this from everywhere. So maybe it's from morning. Everywhere. Technically, it's it's, we've got afternoon for me, evening for Kevin, and morning for one of our wonderful viewers out. I'm sure somebody
Speaker 0: is more important for you. Good good morning from 9:30 at night. Okay. So so so I've already had to think about how how this should probably shape up, which is this return move this return move operation here does some work, right? It picks the move and then it returns it.
I think we should decouple these. So we should do one final step which picks a move and then the final step which just returns the move. And then we can put in like an inter we could put in one additional operation in which will store data in a database. I think that's probably the, you know, we have to decouple it and then we can we can take in the entire board state from the trigger, store that and we can store the move that we ended up concluding before we send it on. That's that's what I think we should probably do.
There is a question of what we want to store. You know, we also have this additional start and end move point. I think end always returns the final state of the board. So there's something to be to be said about that as well. Yeah.
What do we what do we want to store? Maybe we start with we we just store the the move. Like, we start really simple. We just say what is the final move we chose?
Speaker 1: Yeah. I think that's probably easy number 1. I was also gonna say we could record win or loss, like, if we wanna go really simple. But technically, I think move is probably simpler because then otherwise, we're gonna have to parse through the JSON data that we get back. So yeah.
Let's do let's do move and then maybe, like status of the game and, and then go from there.
Speaker 0: Interesting. Let's just let's just go till we can't go anymore. Now just as a note, there is a limitation that we have to work within with directors. Now we actually just did a different directors TV episode on this. It was request review, filtering JSON objects, I think it was called.
But, basically, if we store a JSON blob inside of a field in directors, we can't filter inside of that blob. It is just a string. So what we'll need to do is extract specific values and store those as individual values if we want to use them for insights. We just have to be aware of that. Let's let's start by doing what we said we would do, which is adding this extra, splitting this out into 2, and then we can figure out how we're gonna store data.
I think that's probably the safest bet. So, let's add a new operation here. Let's call this 1, determine move. I think that feels okay. I think we'll give it the key of move.
And I don't know why I've started doing that without having copied out this. So determine move, and this will be a run script. You can copy and paste that in here, except all we're gonna return now is move next. I think all we need to return is the strength next move. That's all this one has to do.
And then we're just gonna re kind of configure these. So this one's gonna go here. This one's gonna go here and we hit save. And this keeps happening. Don't know why, but it seems to work if you just kind of what was it?
Do it again? Yeah. Because that's what it was.
Speaker 1: Disconnect it and then save and it should work. Yeah.
Speaker 0: Like this. We do. Well, we did that. Okay. Cool.
Cool. Cool. Cool.
Speaker 1: Yes. And now we're
Speaker 0: And then, which feels odd. Now today, there was actually a release of Directus the day that we are recording this, but I'm not feeling brave enough just just in case. Just saying it. So this this may well have been fixed. Do we remember what it was?
Because it ain't ain't Don't say these. Bloody words.
Speaker 1: Is it not working? See. Hang on. Hang on. Okay.
What if
Speaker 0: we go this way?
Speaker 1: Yeah. Okay. Fine. And now switch it.
Speaker 0: I think we had to, like, maybe.
Speaker 1: No. What the heck? This this resolved with this last time. So this is
Speaker 0: Yeah. But nothing nothing's changed, Andrew. Nothing's changed.
Speaker 1: Okay. Go back, and, like, remove it from there, save, and then go back, go out, and come back in.
Speaker 0: You think?
Speaker 1: I don't think that's what we did, but I wonder if
Speaker 0: it will No. No. We we definitely didn't do that. We did this and then this and then
Speaker 1: This is why you should always watch your VOD recording if you're trying to bug
Speaker 0: some of the buttons. We fixed it. Watch your VOD recording. We did. Oh, no.
Speaker 1: Okay. Remove the okay. But so let's maybe there is actually an error in there. Maybe there is a new error that we've introduced.
Speaker 0: Alright. But not nothing new has happened. Right. Value fulfilled resolve in collection, direct to this operation has to be unique.
Speaker 1: Are move the same? Is determine move and return move? Is it the same key? Is that what it is?
Speaker 0: Oh, possibly, but I really don't think so. This is no. This is called return move.
Speaker 1: Nope. Nope. It's a return move. You're right.
Speaker 0: So it works that way around. Cool. Cool. Cool. Cool.
Cool. So so then we do this. And it does It's this. This is this is where there's a problem.
Speaker 1: I feel like our logic is not going to work. Can we just trigger another what if we just trigger another flow instead of doing it past the data? I know it's not as clean but, like, if we're gonna, like, bug solve even just temporarily, why don't we just send it to another flow? It's a new thing we haven't looked at as well because you can trigger
Speaker 0: another flow. I don't wanna do that. I don't I I want it to work.
Speaker 1: I mean, we all want these things to work.
Speaker 0: Okay. We're gonna pause and and we're going to come back when we have worked it out.
Speaker 1: What was so wait, what was, what was the you're right. The solution was exactly what you described last time. Are you getting error logs in your console that are different than that?
Speaker 0: Oh, no. Really? Shit. It's a great way to start a bloody director's TV thing, isn't it?
Speaker 1: Fantastic. Bugs are what make this feel interesting. K. Oh, what?
Speaker 0: No. No. No. This this one was the good one.
Speaker 1: Oh, that's yes. That's the reversed way, so it won't work that way.
Speaker 0: What have I just done? So this goes into return move. No. We want this one to go into determine move. Determine move goes into return move, save error.
Speaker 1: An error. Delete that card and create a new one and see if it solves the problem.
Speaker 0: Call return move. Card. Oh, doesn't like that.
Speaker 1: Yeah. Just return. There we go. Same thing. Director's operation.
What is okay. Can we see that that field go into the data model? Can we not see that operations logic?
Speaker 0: I mean, I mean, yeah, I guess. But, like, we're looking at quite, like, low level, like, built in system. Okay. Cool. There is an error.
Copy to action. This is what Riker's just messaged. Hang on. Did it work? No.
Okay. I'm telling him what happened. Right. I meant I'm talking to the lead maintainer. Node 13 were.
I made node 2 then changed the nodules.
Speaker 1: And
Speaker 0: okay. Fuck. Fuck.
Speaker 1: This is the way to this is definitely what what what was meant to happen this week. Is it okay. What if we run a simple script? Like, just detach that new one that you just created. Add any random script after the determined move.
Okay. So we need to create
Speaker 0: a new one first of all. Right? Yes.
Speaker 1: Yes. It's
Speaker 0: great. Do will return data. Great. Right? It's like a pass through.
It does nothing.
Speaker 1: Is it the script? What if we do the same thing with the I Yeah. If we duplicate this script, can you duplicate a flow?
Speaker 0: Not a whole flow. Oh, yeah. Flow? Yeah.
Speaker 1: I wonder if duplicating the flow might solve this issue.
Speaker 0: Duplicating the flow will mean we have to reconfigure the extension. But
Speaker 1: Well, I mean, let's let's see let's see if it works.
Speaker 0: Well, yeah. Well, I don't know why I'm doing that. So return. Okay. And we'll copy out.
Blows. And then we can
Speaker 1: Oh, no. I actually can't duplicate 1.
Speaker 0: You could, once upon a time. No. It's insights. That's insights.
Speaker 1: Good to know. Fucking Here we are. Now we'll figure this out. We got this. I have I have faith in us.
Okay. So our regulars our regular script
Speaker 0: our no.
Speaker 1: Our regular script was working. Okay. Skip just to the end. So, like, remove the the head to head and go straight from avoid body to the very end. So skip over the avoid head to head because this is where we started to get these issues.
No. It's the final card that's fucking this up. No matter what it is.
Speaker 0: It's reassign.
Speaker 1: It worked. Did it work?
Speaker 0: It worked. Don't question it. Okay.
Speaker 1: It worked. We did it. What what was our solution, Kevin? We just tried many times and then waited to see what happened. Just wait until I cause many problems to my team.
Okay.
Speaker 0: On our screen. Okay. Alright. Gonna resume recording. Thanks for the click.
Speaker 1: We got there. We did it. Go team. Go, Kevin.
Speaker 0: Okay. Hubert, you're welcome. Alright. Ready to come back?
Speaker 1: We're ready. Let's do it.
Speaker 0: Okay. And welcome back. We fixed it. We don't know how we fixed it. It just started working again.
We will undoubtedly come across this again. Having spoken to like our lead maintainer, he thinks it's a bug, which I will open a GitHub issue on after this recording. Okay. Wicked. So now determining the move and then returning the move.
Okay, cool. So we determine the move, which just returns the next move. And then all we want to do here, the literally the only thing we want to do here is return last. Right? Data no.
Last. Last? Yeah. Just last.
Speaker 1: Yeah. I think just last.
Speaker 0: Date data dot data dot last.
Speaker 1: Is it? Alright. And the reason for this, if if nobody realizes this already, is because, like, you have to feedback this very specific JSON structure into the response that that Battlesnake precedes. Which kinda sucks. It just seems like, it should.
It should just read this the engine should just get it. It should understand at a core level.
Speaker 0: If you're if you're being told to implement an API specifically, it's fine to have to do it specifically, but we'll test it works. And in theory, we should be able to in theory, we should be able to add another another flow operation here to do logging. Any logging we want at the end of the whole run. So let's manage our battlesnakes. Let's create a game where I'd already set a battlesnake and a hungry bot an 11 by 11 board, and we'll start the game.
And that should be running game in progress. I can kind of see my logs over here that the game is still happening. I'll wait for the game to conclude, which it has not yet. You know what? I've run out of patience.
Oh, no. It happened. So let's hit.
Speaker 1: There we go.
Speaker 0: Excellent. It did end up doing itself in, so there is something wrong, but we're not we're not looking into that bit right now.
Speaker 1: It worked. That's the important thing.
Speaker 0: It it worked for some period. Anyway, that's all so it is it is talking successfully, to directors, which is useful. Now what we now wanna do is figure out what we need to log. And before we do this, let's create a collection for it. So we'll go to our data model, we'll create a collection and what are we going to log?
I don't think we should log every turn. I mean, we could log every turn.
Speaker 1: Well, I I mean, we could. I think I don't think it's necessary either. I think let's let's let's right now just log the final move just as like a getting started place. We're just gonna pass that data right through. And then let's do at like, ultimately, we'll end up adding something around, win or loss, because I think there's something cool visualizing that.
And then I think there's honestly, I'd be love to explore us actually pulling the game GIF from this, which I think is more logic, but I think it's very interesting should be pretty straightforward.
Speaker 0: So if we want to log this, if we wanna log the the game, we need to be doing this actually in the end endpoint. Right? We don't want to be doing this in the move endpoint. We want to do it in the endgame. So this actually means we need to a little bit more fiddling, a little bit more fiddling with directors.
And to the dismay of our lovely editor, I'm actually gonna unshare and reshare, but I'm gonna do my whole screen this time because No camera job. It's too late. I'm doing it. It's done. It's gone.
It's done.
Speaker 1: Farewell. It's it's gone. Farewell screen.
Speaker 0: Okay. Apologies for for for that. But what we have here I'm gonna just reconfigure also.
Speaker 1: See every day, Kevin, by the way? Is this your screen, this large massive thing?
Speaker 0: It's a 49 inch ultrawide monitor, which will feature in a future episode of What's in My Doc because the final episode of the season is yours truly. We talk about it more then. So if I just make this bigger, we want to we have this move endpoint here. We can delete all of this logic that was left actually. But we have let me just shrink this down a bit.
Just a reminder, when we hit the root endpoint, we're gonna hit this flow. When we hit the move endpoint, we're gonna hit this flow, the start and there's end. So what we're gonna do is basically add this logic to to end. So what we're gonna do is, kind of similar to the info end point. You know?
The info end point. Yeah.
Speaker 1: Yep. Yep. Same thing.
Speaker 0: Yeah. Although that is a get endpoint. We need a post endpoint. So it's more similar to move, I guess. But what we'll do is we'll create a new one.
We'll call this snake, end. This can be based on a web hook trigger. It will be a post request. It is a post request. Right?
I mean, we'll
Speaker 1: find out together. I'm pretty sure it's a post request. Yeah.
Speaker 0: We we will, but I'm also gonna I'm also gonna double check.
Speaker 1: Look at the documentation. Probably smart idea.
Speaker 0: Look at the documentation.
Speaker 1: Not there though.
Speaker 0: Webhooks. Here we are. Game over. It's a post request as an error hit, by the way. It's it's not formatted as a code block, but it's a post request to slash end.
It will be the game object, the final turn number, which is useful, the board describing the initial state of the game board. That doesn't feel correct. Oh, on that final turn, I guess. And you describing your battle snake. And I wonder if there's a death reason, but we can find out.
So response body date of last operation. Great. And first thing we're gonna do is just copy this URL. I'll hit save. We'll copy this URL and we will go ahead here and const flow response.
I'm not sure we need to, respond to this, but we will Await fetch full URL. We're basically just gonna copy and paste the all of these options because they remain the same. And then we're just gonna send okay. We don't need to do like we actually don't care about the flow response. We're just gonna send all the data by fetch and then send okay.
We just need the data. We don't and eventually to respond. So we'll save that.
Speaker 1: Hear that flow response? We don't need you. We
Speaker 0: don't need you. Yeah. So inside of our extensions, director snake, we'll run npm run build. Oh, in today's release of Directus, we squash the bug that stopped the autoreloading working. So That's awesome.
Nice.
Speaker 1: Was that because of us?
Speaker 0: No. Not even remotely. Well, I'd like to think it is. 10.9. 10.9 came out yesterday, so it resolved that.
So now we have our new extension. With this new endpoint, we don't need to do anything more there. Back over to our code editor here. I suppose we can just we can actually just leave this be. Yeah.
And we can Yeah.
Speaker 1: Let's just log, see if it shows
Speaker 0: before. It. Yeah. Exactly. Create rematch.
So that whole game is is running. We see that 200 all the way. Really low really low, response. That's great. Running, running, running.
And then we now no longer, we remove the console log for end. Oh, this is on start. Have we done it in the wrong place? Everyone, we've done it in the wrong place. I know.
Amateur hour. But it is logging it, so we know what we know what the shape would be. Yep. So we can do that instead. We'll make this async.
We'll remove async here. We will go and rebuild the extension, and then we'll restart directives. And while that's kinda happening, we can we can take a look at this object here. So we have game, which is an ID, which we'll probably wanna store. The rule set perhaps, probably not for this one, The the final turn, 95.
Don't know what we wanna know about the board probably because 95, it's the turn where you're dead. So that's that array will not include us or it will include us and we'll be the last one standing, I guess. You has health. So I wonder oh, maybe we won this one. Maybe we won this one.
Let's, let's watch it. Have a sneaking suspicion we did win. Yeah.
Speaker 1: So wait. How did we how do does it show that for us in that result? Like, it doesn't it doesn't say, like, in our in
Speaker 0: the law No. No. But I I I wondered why is it still giving me a health of 90? Why did it give me a health of whatever it was, 83? Smart.
I have a sneaking suspicion if you die, the payload looks different, but there's only one way to test it, which is I've now restarted Directus. If I refresh this, there should be some logs. So this was the log of the one we just accidentally ran when we shouldn't have. So this is the start. So we'll just forget that one.
Let's, let's do a rematch. And now we should only get the response when when it dies. So we can see this. We see it's posting slash snake slash move. And the last thing it should do is post slash snake slash end and it should hit a different trigger.
So we'll just watch that happening and this is happening real quick. Wonderful. And that is what happened. It had a different flow at the end slash snake slash end. So if we go back to our flow now and refresh, we should be able to look at the logs.
Payload. Okay. So this oh, did we win or or lose? That's probably worth knowing ahead of time. You won
Speaker 1: again. Come on, ready, set, battle snake. What are you doing winning all the time? You're too good. You're too good.
Speaker 0: Too good. So 73 turn 73. That is us, ready, set, battlesnake. We know our ID. I wonder if there's something in here where we log what our own Battlesnake is, but it may not be necessary.
Let let's keep looking. It has health of 92. It has a length of 6, which could be interesting. We don't need to know what all the food is, where all the hazards are. You is just a convenience again.
Alright. Let's, let's literally just keep creating rematches till we die. We'll let this run-in real time which means we we're gonna have to wait a hot minute as every move happens. Are we too Lindsay.
Speaker 1: Are we too good at programming Battlesnakes that he will live forever? Is that are we gonna need to are we gonna need to, pause?
Speaker 0: Artificially. I'm gonna have to artificially make him just, like, die.
Speaker 1: Come on. I think we oh my goodness. Come on. We gotta do it again. Gotta play again.
Okay. Okay. Come on. 3rd time's the charm. We did, we did too much, Kevin.
We went too hard.
Speaker 0: That, game in progress thing is not behaving, so we'll restart a game this way. I mean, if I just add ourselves, eventually, we'll die.
Speaker 1: Oh, there you go. Yeah. And it's interesting too. So, I think part of the game in progress thing might be I know I know Battle Sneak scaled down the server load, so it may be something related to that. Maybe.
But, yes, we should live for we're gonna live forever. That's really what you've just done here. You're about to, you're about to kill your local version of Directus just because this will run forever. Hey. Good time.
Alright. I immediately got it right and wrong. Thank you. Thank you. I reset Battlesnake.
Alright.
Speaker 0: I mean, eventually, we had to lose. Right? So let's look at the final log when we lose. And I have a hunch. It is that our battlesnake does have a health of 97.
So there is a health in there. So that isn't how we determine whether we live or die, unfortunately. We have a board. We have you. Although, where are the oh, the snakes array is empty.
So we are not so we are there. Sorry. But we are not present in the snakes array.
Speaker 1: Oh, okay. That makes sense. So if the snake array is empty, we didn't win. If we are not empty
Speaker 0: if we are not in it. Yes. If we are not in it. So the easy the easy thing to do here is we have a we know our snake ID. Here it is.
We could store it in a collection of our snakes to like factor out or we could just hard code it in.
Speaker 1: Well, let's let's let's have some fun. Let's let's do the more challenging
Speaker 0: Sure. More challenging. Snakes. And then, actually, if we look at the if we look at the API, I don't think there is a a meta Battlesnake API is there to get information about games and stuff. No, that's fine.
What else should what there could be. Yeah. What if there is there's a start endpoint. I wonder if no, it's fine. We'll we'll we'll hard code this snakes.
All we care about snakes is that they have a manually entered string as the ID and we can add all the others later. So if we go to our snakes, we'll put in the ID. That's ours. And you know what? We'll just add a name to it just so we can tell what it is.
Right? So, this battle snake is ready, set, battlescape, I actually think is its actual name because we wanted to distinguish the one you made to the one I made. And my name is Elsford k. So it's registered Battlescape. Okay.
Cool. So we've got that. So what are we gonna do inside of this end flow? First thing we need to do, I suppose, is we get this whole payload. The only thing we wanna enrich it with right now is are we alive?
So Yeah. Is alive. Did live. Did I live? And that's going to run scripts.
No, it isn't. First of all, we're gonna get all of our snakes. That's what we should do. Read items from the database. So, get snakes.
Permissions from the from trigger. I'm just gonna give it full access so we don't need to contend with permissions. Snakes. If I don't add any IDs, do I get them all?
Speaker 1: We'll find out. We gotta find out.
Speaker 0: This is
Speaker 1: trial and error, Kevin. This is trial and
Speaker 0: error. Real, real trial and error thing going on here. Alright. Let's create a rematch with ourselves. I feel like I should add another snake back in just to end the games quicker.
Speaker 1: So do it. Yeah. Do it. Add another snake in. Just go back and
Speaker 0: make sure I'll let I'll let this game conclude. We'll do it until the next time we do
Speaker 1: it.
Speaker 0: Still running.
Speaker 1: By the way, it looks
Speaker 0: like games. Oh, thank you. I actually was really trying to find, like, the end of Loki season 2, all of the timelines. I'm not sure if you've seen it. And so it was kind of Yes, I've probably seen it.
I've seen it's going for. Yeah. It was kind of that vibe I was going for. Okay. So obviously we eventually die.
That's just the nature of playing by yourself. Let's just see how they perform now. Okay. Again, no idea why. Really shouldn't be doing that.
But
Speaker 1: Don't question it. We wanted that to happen. We willed it into being. We willed it to happen.
Speaker 0: We want you to be chitter. Alright. There there's all of our snakes. So that's great. So that that returns an array of objects which contain IDs which are our snakes.
So the next thing we want to do then is did live, like, you know, did live. And we will run a script here. And all we wanna determine here is, what did we call the last one? Snakes. So data dot snakes is the array of snakes and we want to know and we want to know did the trigger dot I need to I don't want I don't wanna close it.
Don't wanna close it to to take a look. Let's look at the docs. Webhooks. So it will be trigger dot, I think, payload.
Speaker 1: I think so. Yeah.
Speaker 0: Yeah. We can always fix that later. The payload.board.snakes. So we wanna see, did that contain that? So, JavaScript, does array contain other does array contain specific item?
I think it's like includes yeah. Does array contain item from other? Right? I'm pretty sure we just did this in our last episode.
Speaker 1: I think so. Yeah.
Speaker 0: Some. And I think I used find instead. So, live, which is like the if it lives, it will be found. So we'll say is inside of data dot snakes, is does that contain yes. Does that contain does data.snakes contain?
And this is where it gets a bit honky, and I need to just just have a moment to think. So we wanna know out of all of the items in the snake array of which of which we know the I the ID is gonna be snake dot it. We know that already. Does is snake ID equal or does snake ID I got it. Does snake ID feature inside of this array?
So what we actually wanna do is return whether this array, the one with all the snakes on the board contains, which I think we've determined is includes includes.
Speaker 1: Yep.
Speaker 0: Is it includes? I think we need to simp I think we need to do one more step beforehand which is take this big array here and just simplify it down to IDs. So, const IDs, IDs on board. So we'll take all of that and we'll map it and all we wanna do is turn that into an array of IDs. So it's just IDs.
It's nothing else but IDs. Forget all the other information. That's all of the boards. We don't That's all of the IDs of the snake on the board. Exactly.
And then we will return whether or not IDs on board that's it. IDs on board includes any of our snakes IDs and we will return to live. So true force, true false, did it. Really?
Speaker 1: It's gonna work. First time's the charm.
Speaker 0: Oh, no. I've done it again. I've hit create rematch, But eventually the answer will be false, right, this time with confidence. It should be. Or it'll just live forever and this will be this will be our time.
Okay. Hit its hit its own tail. Mounted itself into a corner and did itself in. So let's, look at the last log. Cannot read properties of undefined.
Okay. Cool. And it was body, by the way. It was trigger dot body. So that will probably be why.
Trigger.body.board.snakes.map. So let's, let's just check that. Body dot board dot snakes. Cool. So we will create rematch.
Speaker 1: And now second time.
Speaker 0: And now.
Speaker 1: Go, red snappettlesnake.
Speaker 0: So stop eating food. Stop eating food. As it gets towards the food, I'm like, no.
Speaker 1: Come on. What are you doing? We don't want you to live anymore. What are you why?
Speaker 0: Why are you doing this? This battle snake is not bad apart from those times it randomly goes off the edge of the board, and we don't know why. I think it's just got
Speaker 1: a it's got a it's got a spirit of its own.
Speaker 0: Oh, what's happened there? Oh, there we go. Oh, okay.
Speaker 1: There was some real They
Speaker 0: were clearly in a queue or something.
Speaker 1: Goodness. Wow. We're doing I Oh, there we go. Why did you do
Speaker 0: that? I don't know. Oh, yeah. Yeah. Yeah.
Right. It knocked itself into a into that kind of spot where where it can't. There is no viable move. Right. So we'll look at this.
Cannot read properties of undefined. Okay. Okay. What is undefined? What's undefined is what we're running find on.
So let's take a look at that. We're running find on data dot snakes. Okay? Except oh, because we call it get snakes. Fair.
We'll call that snakes. Great rematch. This is the only thing when you'll see ready. Set up
Speaker 1: the snake. Sorry. This is the only thing when you're debugging. Go on. And you were saying something valuable as part of this conversation.
I was trying to add
Speaker 0: some color. Wait. You there's there is just a bit of waiting here. Right? I suppose because the rules engine is open source.
I imagine people I think people have built, like, line emulators that can obviously execute this way, way, way quicker for the sake of debugging. And I see the value in those tools.
Speaker 1: Yeah. You can actually do it in the CLI. In the CLI, you can actually play full games and see them.
Speaker 0: Because we just had to weigh a 153 turns. Alright. Trigger is not defined. Still. Okay.
Trigger is and you know what? Because it's data dot trigger. So many amateur amateur hour little mistakes today. Never.
Speaker 1: Never, Kevin. Amateur is, is a term that we need to take back as a positive
Speaker 0: As amateurs.
Speaker 1: As amateur. As amateurs, yeah. Alright. Come on, ready set battle snake. You can die.
Don't survive.
Speaker 0: Please don't.
Speaker 1: I believe in I I don't believe in you. I feel like he thrives.
Speaker 0: So the weather so the weather today.
Speaker 1: Yeah. Oh. Oh. Is that is this it?
Speaker 0: No. No. It it will lit. It's just obviously saving up moves. Yeah.
There we go. Woah. It's like a taunt. That was like a taunt to us.
Speaker 1: Come on.
Speaker 0: We're doing exactly as we the thing is we made we made this snake. We made this snake.
Speaker 1: I feel like we is a very, oh, there we go. Alright. He made himself, really. Alright. Let's see if this worked this time.
What do our logs say? Hey. We got data.
Speaker 0: Yeah. But this isn't what we want. What do you mean? What we want. I don't know, but I just want, I just want, I just want a true false value so something's not right.
Speaker 1: But wait
Speaker 0: So, yeah, go on.
Speaker 1: Well, I'm just like, what are we returning? Sorry. It's the text
Speaker 0: is really small.
Speaker 1: Not because of your
Speaker 0: screen. It's because We are returning, the whole, like, body of the script, which feels a little bit weird. And I have a hunch. I have a hunch. It's oh, no.
Did live. This is live. So what's going on? What's going on there?
Speaker 1: Super weird. That shouldn't be returning that.
Speaker 0: No. It should not. Let's, look at our previous flow, the move one, and just maybe not, like just take a little bit of yeah. I mean, if we take all of this, and I'm just doing this as a comparison point just so we can kind of dump it in here and take a look kind of side by side, if you will. Yeah.
Data dot trigger, Return. We do just return a value. Maybe, let's take a look at, array find method. Let's just understand this a little bit better. This returns the 1st element in the provided array that satisfies the provided testing condition.
If you need an index, you can use find index. If you need to find the value, if you need to find if a value exists, you can use includes, which actually I think is what we want because that just returns a true or false. So maybe we just swap this out for an includes, includes, And we wanna know yeah. It's like a nested includes. Fine.
And then we return I'm just I'm a little bit confused why it was returning
Speaker 1: Yeah. The whole code.
Speaker 0: Well, we no. You know what? We also don't get here. We don't get a returned payload, which makes me think there was some kind of error happening here that wasn't being captured correctly. Is there anything in here?
I wouldn't imagine there is, and it's now really hard to tell. Let's, let's create a game. Let's let's make this game a lot, a lot quicker to end.
Speaker 1: Hey. There we go.
Speaker 0: Well, do you see how quick that was?
Speaker 1: That was fast. We are smarter than hungry.
Speaker 0: Niamis.
Speaker 1: Come on. What are you doing, Raze at Battlesnake? We want you to not die. We just want you to die.
Speaker 0: We might we might have to nip out some of the logic or something.
Speaker 1: I've never had a
Speaker 0: Well, well, that will that that will be the end. That will be the last move when it finally catches up with that with with Toast. Yeah. Snake end. The game seems to be struggling with the concept of ending, but it has ended.
Alright. Yeah. Look. We get the options here, but we don't get the payload. Because if you look at the other flows, for example, wow, 1632.
Wow. You see that all of these run scripts. I know they have the payloads. They have the options and they have a payload. Options payload, determine move interestingly interestingly, does does not have a returned payload.
Is there something about them returning objects and strings versus just returning like a primitive. Maybe that's what it is. So if I so if I because this could be fine, you know, return. That's returning an object. Right?
Mhmm. Let's create a rematch. Okay. Our snake just went up and up again. Hey.
We don't know why happened.
Speaker 1: Bad things happened. We changed we changed the code. Shouldn't have done that.
Speaker 0: Changed the code. Shouldn't have done oh, there we are. Did live force. Okay. Awesome.
Cool. So so there was actually no problem. Just an interesting note there that for it to show up in the payload, it needs to be an object or an array, I presume. So just a good just a good note there. I would love to play a game where we don't lose.
So we can just so we can just check both sides of that equation and that they make sense. Let's create small boards.
Speaker 1: If we play with ourselves It wouldn't then technically, we'll always win and always win.
Speaker 0: Oh, well, yeah. But then I think it's just gonna get a little bit weird. There we are. Hey. We no.
Wait. That's not what we want.
Speaker 1: That's what we want. But We've wanted to lose so much that now to win seems like a wrong thing.
Speaker 0: So did live should be forced. Yeah. Live should be forced. Let's create a rematch. Maybe we need to put in a a a a less clever snake.
Goodness. Okay. Try Do you want the right bot? Yeah. Yeah?
We we haven't we haven't gotta live long to make that work. Okay. Great. So now if we refresh, we look at the logs. Did live hopefully should be
Speaker 1: False. Absolutely. That's totally what we were wanting to have happen.
Speaker 0: Great. Is this really what we're gonna spend this episode doing? Okay. Fine.
Speaker 1: That's it. Just debugging JavaScript. That's really what we're here for.
Speaker 0: Right. All we want to do is now maybe maybe we just roll back for a sec. We're doing all of this grab snakes thing. I don't think this is adding I've deleted it. I've just done it.
I've just done it. I've been busy for decision. Exec executive decision. All we care about is this ID. Right?
So let's just we'll we'll delete this. We're gonna
Speaker 1: hard code it in now. Okay.
Speaker 0: Yeah. Yeah. Like, I don't think it's worth, like, going at all costs. Right? So all we want to know is does data dot snake say const?
In fact, I think we can say, does snake include data dot trigger dot body dot board dot snakes dot map. Sure. So that's just a set of IDs. And all we wanna know is, does IDs onboard include this? And I'm I'm assuming can I console log inside of these?
Best bit of, debugging we got. Great rematch. Well, I think our snake's broken. I think we spoke too soon about the capability of our snake. In it doesn't like includes.
Payload.body. Right. Hang on. It doesn't like includes, which is this one here. And we don't get a log, annoyingly.
So all we care about, Let's, you know what? I'm actually gonna be bold here and just remove this, and we can start we can start again. So so all we wanna know is did it live? And to know if it lives, we're gonna go through oh, no. I do need to I need this too.
Speaker 1: I like it. I like the I like that you were confident in the removal,
Speaker 0: but also
Speaker 1: truly confident. That I you
Speaker 0: know, I really wasn't. So all you wanna know is, does data dot trigger dot body dot board dot snakes, does that include includes includes let's take a look. We need to give it a specific value here. So I think what we'll first do is we'll say snake IDs on board. We'll take this and we'll map it once again.
So we just extract the IDs. Then all we wanna do is check whether snake IDs on board includes this value here. It's all we wanna know. Please work. Create rematch.
Speaker 1: We've got this.
Speaker 0: Okay. Our snake just keeps going up by the way. We fundamentally broken something.
Speaker 1: But I mean, these things happen. These things happen. We'll figure it out.
Speaker 0: We've got Live force.
Speaker 1: The rest of today.
Speaker 0: Live force. Okay. And we did we did we did live force. Yes. That is that is true.
And all we need to do is outlive the snake, which we did, and hopefully live is true in this context. We may need to go back as as we commented in Okay. But we lived, but we lived. Let's let's take a look at the payload. Let's take a look at the payload.
So body dot game, no. Body dot board dot snakes has ready set battlesnake present, and that's it. The snakes have an ID. That's our ID. That is I'm just gonna double check.
Yep. The ID, nope. Not the ID we've been pasting in. What does it get a different does it well, it's different. So I'm wondering, look at this ID, w d at the end.
If I look one game back, it gets a different ID every time, Andrew.
Speaker 1: So we gotta pull name then, I guess. I guess the ID is not
Speaker 0: I'm literally so mad. Yes. Fine.
Speaker 1: As well, you should be. Damn you, battle stake and your your ID that is not truly the snake ID. Who do
Speaker 0: you It does feel that way. I can't believe we sunk so much time into that. Okay. Cool. No.
Right. Create custom game. Create rematch. Okay. We lost because apparently we just go up now.
Yeah. Liveforce. Cool. And create rematch. Yeah.
I mean, we lost again. Sure. Because that's what's happening now. What is going on with that, by the way? What is going on with that?
Is it is it work. Are we struggling under the number of logs that are being generated?
Speaker 1: No.
Speaker 0: I mean, possible. Look, avoid head to head force. So there's something in the avoid head to head. Oh, god. Because there was no head to head issue here.
No. There wasn't. Let's create rematch just until we get a a seed. I think, yeah, we both die at the same time there. Let's create rematch.
Let's just create a seed just to test out this logic. Yeah. That's good. Nope. Not good.
Okay. Create game. Okay. We we broke it. We broke it.
Okay. We are still not gonna do well this oh, what is going on? There we go. Is it
Speaker 1: there someone
Speaker 0: is there something is there something about we did? Is there something about the board size being different that's throwing it off? In which case, I'm quite happy to say how Battlesnake only operates in 11.
Speaker 1: That's the right answer. Don't make it work on all the boards. Only standard class.
Speaker 0: True. Okay. Hey. There we go. Finally know whether it lives.
Now it's all about logging information. So let's look at this payload and see what we have. So we have a game ID, which I think feels like a no brainer. Right? Yeah.
And you said we can do something with the ID to get the GIF?
Speaker 1: I believe so. I'm gonna go check and see something right now. I'm gonna go over while you're doing this, I'm gonna go over to my other screen. But, yes, we'll pull back so that we need that.
Speaker 0: We know the turn of the final move, which is 2. Yep. Right. So that's the term where we were victorious. We know that we lived.
We know our length, I suppose.
Speaker 1: Yeah. I think so.
Speaker 0: Yeah. And maybe we do maybe we start with just games. Games get an ID, which can be a manually entered string because we'll we'll pop that in from the API. And I think all we need here is win or lose, right? Win.
That's a Boolean. We wanna know what about it. Like, a turn, I suppose. That can be an integer.
Speaker 1: I mean, game mode? Turns. Yep. Turns.
Speaker 0: Turns feels better. Yep. Turns. Into game mode, but we're not playing around with other game modes. So I just think That's true.
Yeah. It's fair. Yeah. But but the idea is, in theory, you could you could store all of this. Wind turns.
What else have we got at our disposal? Let's take another look at our end state here. We have live die. We had turns. We can start with how many snakes there were on the board from the trigger.
So snakes dot so trigger trigger body board snakes length. Just quickly gonna go, store. We're gonna prepare data. So trigger dot body dot board, did we say it was? Dot snakes.length.
So I'm just gonna pop that in there for later. Yeah. So snakes, that can be another integer. What what else what else do we wanna know?
Speaker 1: I mean, let's let's let's stick it with that. Good because we
Speaker 0: you could do, like, a win ratio. You could do, like, average turns per game. Like there's lots you can do here. Do you continue getting data if you are not the last to loot? Like if you die and it goes on a few more steps, do you only get the end state on your final turn or do you like, let's take a look.
Your battle snake will whenever a game it has playing has been ended, use it to learn how you battle snake won or loss and deallocate any server side resources. Okay. This was like a good starting point. So this is what a game looks like. ID win turns snakes.
So going back to flows, let's, do our end. So did live. We're gonna prepare data. So the only extra thing we wanted to know here was snakes. Snake count.
What else do we wanna know here? We we might as well get it already to be fair. Might as well just do it straight within here. So snake count. Is this.
We have we had snake count. We had turns. So once again, let's just take a look at the it was game. I think it's game dot turn. Game?
Maybe not. Bored?
Speaker 1: Bored? Bored?
Speaker 0: Hold on. No. Am I using it? Oh, it's right here. At the root, turn, integer.
So that should just be trigger.body.turn. And we wanna know we wanna tell how many things. We wanna tell how many turns. We wanna tell whether we lived or died that we needed the ID. ID.
So trigger dot body dot the ID. I think it's just I game dot ID, I think. We'll find That sounds right. Game dot ID. Yeah.
Nice. And while we're here, let's, let's just also get the did live. So live. Live? What did what did we call it in our director's collection?
Speaker 1: Lip libs? Let's take a look. Was just libs?
Speaker 0: It's called win, actually. Win. Alright. Smart. Win.
So, oh, so in here, we'll go prepare data. We'll go live, but it's called win here. Live, live. Fine. Whatever.
So we have, I really don't like using last or I can help it because it allows you to obviously move things around without your data becoming like brittle. So I'm gonna pop did live. And all of these have to be prepended with data. Okay. So in theory, that's the preparation of the data.
Let's, let's run a custom game one more time. If our snake performs well, it will have been the 7 by 7 board that did it in. Well, I think I think oh, of course, we've just got let's create the game with the ready set battle snake and the hungry bot now. Yeah. Weird, isn't it?
Anyway That's such a good idea.
Speaker 1: I wonder what we did. Don't don't wanna know. No. That's a question for, for season 2.
Speaker 0: That was a question. Set back. That was a question for last week of and now it's this week of do you see how big that snake's getting? Oh, of course, it's hungry, but isn't it? That's its whole that's its whole shtick.
Speaker 1: Are we gonna win this one? Oh, we won it still.
Speaker 0: Yes. Legit did. So Well if we refresh here and look at our log payload, snake count 1. This is wrong. So we need to just look at this because we wanna know how many snakes were in the whole game, which in theory should always be
Speaker 1: no. It won't always be this. End. We're pulling from end, so it's only gonna pull the last snake. Right?
Is that what we that's what
Speaker 0: the Yeah. Maybe we maybe we need to do it on start. We need to create it. And then on end, we need to update it. We have turns, we have the ID and we have whether we want, which is true.
So maybe for snake count, because I do think snake count is interesting. We should create just create the data. What do you think? Or do you think it's too heavy handed to create a whole flow for that?
Speaker 1: I mean, I don't think we need a whole flow for it. That seems like a bit of
Speaker 0: a change. I mean, we need we need a flow for it. That's the truth. So may I mean, we we've we've got pretty we've got pretty proficient at it now. So what we're gonna do is create a flow.
Yeah. Yeah. We'll call it snake start, Right? And so what we're gonna do here is we're gonna do this as a webhook. It'll be a post request.
We'll go like this. Just double check the API reference webhooks. Your request to this your response to this request will be ignored. We don't need to care about our response. We'll save this, grab this URL, and then we are going to take this whole end here.
And on the start, we'll make that async. We'll paste that in there, except this time we will change the URL to this 1, 704. So that start, we need to just kill this, go here, rebuild our extension, Done. Go here and Docker Compose up. Wonderful.
Should be able to refresh. Should be able to refresh. Now I think it just took a moment just a moment to spin up there. And what are we gonna do here? We're first gonna save.
We're gonna go and create a game. This will need we don't even need to wait for the game to pan out in order to look at this one log. All we care about is I suppose number of snakes here. So trigger, body, board snakes. Trigger body board snakes.
So what we'll do here is create sorry? Board trigger body snakes. No. Trigger. Shouldn't be Stop it.
Shouldn't be Stop it.
Speaker 1: I shouldn't do that.
Speaker 0: I'm sorry. Create game. We will create data in the game's collection with full access. And all we're gonna do here is we're gonna say the snakes is equal to.
Speaker 1: Trigger body board snakes. I got it. I got it.
Speaker 0: They need to be in quotes because it's Jason. Trigger
Speaker 1: Jason or Jason? I I am a JSON.
Speaker 0: Jason. There is one there is actually, extract snake count. We just need to get the value out in the previous step. I don't strictly think you need to do this, but just to keep it really clean, what we're gonna do here is we're gonna return snake count. Snakes is equal to data dot trigger dot body dot board dot snakes dot length.
Snakes. Copy that key. And then the next one, we will create data. The name and key doesn't really matter because it's our last step here. All we care about here is we need to create an ID at the time.
Oh, but we can create the ID as well. We can get the ID as well at the time of creation. So snakes is going to be equal to, this snake count dot snakes. And I actually think we can ignore this error. There seems to want a string, and I think that's fine even if it's, an int just to stop it moaning.
And then we also want the ID, which is going to be oh, oh, throwing my cursor around. Here we are. Trigger dot body dot game dot id. So if I save this, we create a new game. Oh, got a little Game in progress.
I don't think it is. Our snake 1, thank you very much, but we will create a new game. That's fine. Start game. We immediately should get a new log here and we got a trigger but nothing else happened.
Why? Oh, no. There we are. Create data. Type error cannot read properties of undefined snakes 2 body trigger body game ID.
Okay. Cannot read properties of undefined reading starts with. Interesting. This looks
Speaker 1: Looks good.
Speaker 0: Looks like what we want. Snakes and ID. Is that what we called them? Snakes and ID. What is going on?
So star. Shrek snake count. Because in theory, this is looking oh, pick collection. That'll do it for the access. Not telling it to do anything.
Alright. Create game. I've got really you get really proficient at starting new games with Battlesnake, don't you, when debugging? You do. Alright.
Speaker 1: Did it work? Oh, there we go. Now how That's
Speaker 0: the idea of a new item, item, which means we now have a new snake. Wonderful. So now going back to our end state, we don't actually want to so let's refresh ourselves. Prepare data, prepares a snake count, turns, ID and win. We do not want a snake count.
We do not want a snake count. So when we prepare data, we can remove snake count and that leaves us with turns, ID, and win. Yep. Turns, ID, and win. Turns ID and win.
And, actually, we don't even want the ID here.
Speaker 1: No. We we do want the ID because we wanna be able to pull that GIF eventually.
Speaker 0: But we stored it. It already exists.
Speaker 1: Oh, that's right. Yes. You're right. You're absolutely right.
Speaker 0: This is what we're gonna This is what we're gonna so all we're gonna do here is now update the data Lovely. In the game's collection with full access with this ID, trigger body game ID, and the payload, I think we might just be able to put in lost and I think that's gonna be equal. I'm not a 100% sure, but I think that's gonna be equal to the object. So we'll see. Alright.
The first thing we'll do is create a new game, and then I think we'll be ready to jump into insights and see how that works for us. So here's our snake performing well in these apparently quite controlled conditions of an 11 by 11 board with 1 other snake. Okay. Come on. Some something happened now.
Speaker 1: Come on, hungry. You don't wanna survive. You're not really that hungry anymore.
Speaker 0: It could be all all one either. You both suck. Hey. Hey. So first thing to do is let's see.
So you will notice nothing happened there. I've also, to be fair, noticed we don't have any we just have IDs. We probably do also wanna capture, like, the date, like like the now date that's happening because, we just wanna be able to sort the latest item if possible. Yeah.
Speaker 1: That's fair.
Speaker 0: So we'll create a date time, finished. We can call this. I think this will work. Date time. Sure.
And then at the well, firstly, that didn't work. So we don't wanna spend too too much time on this, but we'll prepare the data here and we'll say what was the name of the field? Do you remember?
Speaker 1: Oh, it was, we didn't call it datetime. We set it today. No. Now? When?
I'm not sure. Jeez. That that's terrible. Both have forgotten Finished. The minutes of creating it.
Speaker 0: I mean, same. Same to be fair. Finished. We also wanna store finished, which I think you can just say new date, new date dot now. Is that how you do it in JavaScript?
Speaker 1: Your guess is as good as mine, sir.
Speaker 0: Yeah. New date dot now. Cool. But it didn't work. So as much as this is cool, what happened here?
We updated it. We updated it with the key. Oh, do you wanna know what didn't happen? It isn't showing here that we had a payload. So I think there was an issue with the payload.
Oh, look, the payload didn't save. Interesting. So I'm just gonna double check that that is what happened there. If I hit save and then step back in, maybe it shouldn't let me do that, but this is okay. We're not gonna lose much sleep over it.
Finished is going to be, no, last dot finished. Then we had, win is going to be last dot win. We'll wrap that in quotes too. What was the third one? What what are
Speaker 1: the 2 that we have now?
Speaker 0: Win, turn Yeah. Turns. I believe. Like
Speaker 1: a living rubber duck.
Speaker 0: Last stop turns. Turns. I'll hit save before we, you know, spend time on a full snake. We'll take a look. Turns and win.
Turns and win. And now we have finished. Turns, win, finished. Turns, win, finished. Great.
So let's save all of that. Let's create a rematch. We should see, hopefully, that this is stored. And then I don't think we're gonna spend much more time capturing data. Instead, we'll run a few games, and then we'll try some insights.
Nothing's happening still. So still something done in our logic. Let's look at our latest I'll just refresh it. Let's look at our latest log. Oh, intermediate value dot now is not a function.
I actually know why this is the case, and it is because, these run script operations running what are known as JavaScript isolates, which are these incredibly limited JavaScript like runtimes that only contain pure JavaScript objects. Let's actually take a quick look at that because I think it's interesting. Isolut. Yeah. This page looks legit.
That's not what I want. I do kinda wanna show you like pure Java script or, objects. This is what I'm looking for. What I'm looking for is just like, where is the list? Am I am I what's happening?
You might
Speaker 1: be imagining. You might be imagining this. You may have already used JavaScript.
Speaker 0: I just I have imagined it.
Speaker 1: Created it. Maybe you created. There's no documentation. What did you call them again? Isolates.
Isolates. Interesting. I've never
Speaker 0: heard of that. Isolates.
Speaker 1: And now is that just a security thing within Directus to ensure that, like, bad actors
Speaker 0: So this So this actually happened in version 10.6. We had I think this is quite what I want. We had prior to version 10.6, you could build, you could use NPM modules inside of extensions. Extensions. Well, video has gone a bit funny.
I'm not sure if the is fine now. It resolved itself. Anyway, and the library which we use to isolate flows from each other and operations from each other basically became insecure. Like, it was it was became known it was insecure, and so we had to remove it. And in doing that, we replaced it.
And now it now it runs what are known as isolates. And this is what I'm looking for, I think. 1st class. Nope. I'm looking for oh god.
It's really bothering me, but I will take a moment to find it. What's is it in our docs? In breaking changes 10.6, we dropped support for custom npm modules in the run script operation. Prior to this release, directors relied on VM 2 to run codes in the run script operations and flows. VM 2 is now unmaintained with a critical security issue that could potentially allow for code to escape the sandbox and access the machine, which hosts directors.
So we've migrated to isolated VM to allow flows to run safely. This basically means you can't use just arbitrary NPM packages in flows anymore. You can still turn them into custom operations, but it isn't isn't here anyway. I learned a lot in during the process of this. There are lots of things you use in JavaScript, which you think are part of JavaScript, things like set interval, things like console log, set time out, fetch, that you just think, oh, this is part of JavaScript, but it's not part of JavaScript.
It's either part of the browser's implementation of JavaScript or Node JS' implementation of JavaScript, many of which are shared. Isolates don't have this. Isolates just have pure JavaScript functionality. And the I I what I was trying to find was the list of what that meant. I've I can't find the word off the top of my standard objects.
Boom. Found it. Thank you. So this chapter documents all of JavaScript standard built in objects. So you have access to global this infinity, not a number undefined.
You have these function properties only. You have some fundamental objects like object function, Boolean symbol, a few error objects, a few number and dates, a couple of text processing functions, a few on arrays, key collection structured data and so on. What you'll notice here is a lack of console logs. What you'll notice here is a lack of date, which is what I was which is what I was going for there. We're not gonna lose any sleep over this.
I'm literally just gonna we'll remove finished. It just isn't worth the time right now. You know, I do like it though. I wonder if can I add can I add after the fact a date created? Or did that have to be done at the time of Based on my I think it might have had to be done at Yeah.
I just scroll down. Though. Exactly. And then just flip it over. I'm just taking a look.
There's a sort field, which is great. That's okay. Take a little take a little cheeky screenshot of this so I can refer back to it. There we go. I'll put that off at the site, and then we'll delete this collection.
I there might be a way to do it, but I I don't have the the patience. If I see But what I want is date created because I think that would be interesting. That's all. I just wanted that one extra field. So half width.
So we also had in here a win, which was a boolean. We had turns, which was an int. We had snakes, which was account. It was a number. It was also an int.
And we had finished, which we've effectively replaced with a date created. So half, half, and whatever. Half. Okay. So but but so they run-in isolates.
And isolates what it taught me was all these creature comforts I have in JavaScript are not in JavaScript, they're in Node. Js or they're in the browser implementation of JavaScript. But, yeah, that's how I basically learned that.
Speaker 1: There you go. I had no idea. I had no idea console log was not built into JavaScript. That seems so odd. But I guess the console you're dealing with is in the browser, so it does make sense.
Speaker 0: Yeah. Let's create a new game. Let's create a rematch, see if we can get it to work. But, yeah, just fascinating. Yeah.
Isolates. And isolates are not, you know, while that's running, isolates Cloudflare. I wanna say Cloudflare Workers. Yeah. Here.
They have some really good reference while I was learning more about it. Isolates. VA orchestrates isolates, lightweight context, provide your code with variables that can access in a safe environment to be executed within. You could even consider an isolate a sandbox for your function to run-in. A single runtime can run hundreds of thousands of isolates switching between them.
And isolates memory is completely isolated, so each piece of code is protected from other untrusted and user written code in the runtime. Islets are also designed to start very quickly. Instead of creating a virtual machine for each function, ISIT is created within an existing environment, blah, blah, blah, blah, blah. So but what's interesting is they are they are way more limited.
Speaker 1: Yep.
Speaker 0: Yeah.
Speaker 1: I wondered I wondered why because I I think what did I I was reading yeah. I've been reading through the documentation, and it said that the the specific JavaScript that you were able to call within within lots of these areas, the directives were limited. And I was like, oh, that's that's an interesting and I figured it was security, but I didn't realize there was so much to learn here. But that is why you come here to Ready, Set, Battlesnake is to learn things about Battlesnake and JavaScript and directives.
Speaker 0: And it worked.
Speaker 1: And it worked. Let's go.
Speaker 0: And it worked.
Speaker 1: We did it.
Speaker 0: So what we're gonna do now really is we're gonna just hit create rematch. And if I can I create loads of games in in, like like Yeah? Yeah.
Speaker 1: Yeah. It's gonna break things.
Speaker 0: We we we have also not built just a moment. We have not built logic to know which game is happening. We'll do one at a time. Top of them, we have. We have.
We have. Because every turn is individual on the move endpoint. And then on the start and end, we're referring to the IDs. Actually, we could be running a load of these in parallel.
Speaker 1: Alright. So give it a try. Let's see what happens. See what happens.
Speaker 0: What's the worst that could happen?
Speaker 1: We end up having to pause our episode because we made a mistake and we have to fix it off camera. That is the really the worst that could happen.
Speaker 0: Oh, I am a bit concerned now because I don't even know when they've completed. That's another problem is I actually don't know when they I've taken a a guess that they won't take longer than each other. Like, alright, that game's over. I mean, let's just see. There you go.
We're not winning every time. We have a number of turns. We have a number of snakes. Let's, let's just create a few more games. Like, I I want a a reasonable set to start with.
All of them are gonna start with 2 snakes. Go on.
Speaker 1: I was gonna say just so if you, like, if you're if you're watching this and you're like, oh, I don't wanna have to, like, do this. It's it's causing my browser. I don't like all those tabs. You can, like, do all this from the CLI too. Like, you can actually go in and just, like, continually create games for the CLI.
This is just easier than going in and setting up the CLI right now.
Speaker 0: And I don't wanna set up the CLI in this moment. So we should actually know if any games are outstanding because, yes, see, there's no turns and no wins on this one, which tells us that game is pending and now it has concluded. So, actually, we do have a little indication of how games are going.
Speaker 1: Look at us.
Speaker 0: So we'll create a few more games with a mixed player count. Okay. And if we refresh Directus, we should see okay, there are a few games still pending completion. If I refresh, 2 games pending completion, refresh 1 game pending completion, refresh. That game is still going.
That game is still going. That game is still going.
Speaker 1: Oh, still going. We're doing well.
Speaker 0: Is that is that the last one we created?
Speaker 1: I mean, we'll find out. It must be. I hope it is.
Speaker 0: Descending. Oh, they've all concluded now. Okay. I think that's good to start. So Yeah.
Let's go to insights, see see what we can do. Snake battle station. Let's pick a little kiss. Pick wonderful. Pick a little thing here.
Is there a I don't is there like a snake? No? Is there is I don't want this game. Yeah. Cool.
And our battlesnake. You know, do you wanna know why green feels like a very snaky color to me?
Speaker 1: It does. Save. That is
Speaker 0: the right one. Snake battle station. So this is, director's insights. It's this little module over here. And here, we can create dashboards.
I don't really use it very much personally, just because my use cases don't demand many dashboards. So there might be a little bit of learning here. You can create panels. Just as a note, this is panels also an extension type. So if you wanna, you know, build on top of these panels, you know, build additional panels, you're more than welcome to.
For this, I think we're going to stick in here. You know, we have the pie or donut chart, which feels like a good starting point. So we're gonna look at exactly what I had in mind. So win, Win. Count.
Sure. Don't care about donut. That's just a display thing. Show labels. Yes, please.
Show legend. Yeah. Pro oh, do you wanna know what? Let let's just see what happens here. Can because I think we can do conditional styles as well.
So win rate. Okay.
Speaker 1: Hey. Dang. That was I don't wanna say that was too easy, but that was very smooth.
Speaker 0: Yeah. Although I we do need a legend. We do need a legend quite desperately.
Speaker 1: I would just always assume the the bigger one is my win rate. That's really
Speaker 0: It it you you are correct. Alright. Win rate win no. Win rate feels good. We can do, games, you know, we could do, the x axis is this one.
Right? So date created maybe and turns, like how well am I doing over time.
Speaker 1: Yeah. Yeah. I like it.
Speaker 0: Aggregation. I don't really know what I'm doing here.
Speaker 1: Well, okay. Just leave it leave it without aggregation and see what happens.
Speaker 0: Yeah. Sure. And we'll call this one what are we doing here? Turns. Turns feels like a weird one.
Maybe we just do turns feels weird. Why don't we do, like, account? Because I think what it will do, that's tell
Speaker 1: us the number of games. Sure. Let's find out. Right?
Speaker 0: Right. Games over time. Of course, we'll be looking at a very micro Woah. I'm not sure this is gonna pan out. This totally is not correct.
Okay. Something. We did a bad thing. Aggregation can be forced as well. Right?
Speaker 1: Can you? Well, set it as false to see what happens. See what that change does. Oh, didn't like that. We are not doing that.
I guess we do need to have Oh,
Speaker 0: aggregation is gone. Okay. So we have turns. We have snakes, we have x axis, which is date created. Do we need something on both axis?
I'm sure. Do we? Oh, maybe not. I mean, it kind of looks like we do. I mean, I'm kind of as you can tell, I really don't make a lot of dashboards because, you know, it's just not it's just not my world.
Maybe if we let's let's let's, see what else we can do. We have I like metric firstly, which is like total games played. That one feels easy.
Speaker 1: Yeah. That's simple.
Speaker 0: Aggregate function, just count. Count. Yep. And, suffix, Games played. Star decimal unit.
Notations standard. Yeah. Yeah.
Speaker 1: Oh. Uh-oh. GraphQL validation error?
Speaker 0: I think I'm just not I'm genuinely thinking I'm just not selecting enough field. Do we need to do a field ID? Yeah. We did. Oh, there we go.
15, 15 groups load. Nice. Sure. Okay. It's not pleasing my eyes very much that, but let's, keep taking a look.
Speaker 1: Yeah. What do we have still?
Speaker 0: Font size auto, which makes sense, or you can make it fixed and then you can add padding accordingly. I do get the auto. Auto makes a ton of sense. We have a line chart. We have time series.
So the collection, once again, games, the group aggregation, oh, group precision minute because we set a few games off at the same time. Count, date field, date created, date range, past 15 minutes. Sure. Value field. I, turns.
Snakes. Oh, I don't know. I mean, can I edit can I can I oh, there it was? Can I clear the value, please? I'm just gonna hit tick.
Alright. Didn't do anything. I've picked I've picked the wrong combination of picked the wrong
Speaker 1: combination of options. We almost we almost did it.
Speaker 0: Did you see how confident I went in?
Speaker 1: You went in. Listen, confidence is half the game here. We just gotta go in thinking you know what you're doing and, eventually,
Speaker 0: you know I'm not really sure. I'm not really showing this bit of director stuff pretty well.
Speaker 1: No. I think it's I think you're doing this fantastic. I like that we've been able to get stuff like we so it's because I said it was so smooth. Hey. There we go.
Nice. And these are showing us?
Speaker 0: So this is showing us the number of turns taken on a minutely basis. And that was where I set loads of games off, and this was just as it was coming down from the last data point that was held. If I do it the past 30 minutes, I think I'll get a slightly oh, maybe not.
Speaker 1: It's a good try.
Speaker 0: Yeah. This is oh, here we are. Automatic based on data. That feels good. Okay.
So 80 turns over here and then 320 turns over here. And then if we create a couple more games now, we'll create a game.
Speaker 1: Yeah. And I do like this. Like, if you were actually looking at this over a period of time, this would make a lot more sense.
Speaker 0: Could be really insightful. Yeah. So let me just save that, and we'll just make sure. Oh, thank you. Woah.
Last game has not concluded yet, so we'll just let that conclude. Okay. So if we go back to insights now, there you go. It's a little bit more of a of a view. Right?
So I don't really wanna look at the number of turns though. I wanna look at is it count? I think that will give us the same graph. Yeah. What I want is I just wanna know how many there are.
Speaker 1: How many games?
Speaker 0: How many how many games? Do you think turns is interesting?
Speaker 1: I no. I mean, I think let's I so turns is interesting, but only if you're playing 1 at a time.
Speaker 0: Exactly. And I think I think I think there's a way to do that. I've seen other people do it. So we had I think it might this global variable variable key can be like game. Confidence.
Speaker 1: Lots of confidence.
Speaker 0: Game interface. That's not what I want. Type. Is it in here? No.
I've seen it. I've seen it somewhere else. Is it maybe called global relational and you pick a collection, you pick games, display template? Yes. You find the ID.
I think this is it. So you select a game. So it says select an item. So can I, whatever? But we we won't look at editing that.
So you select a game, right, like this. Great. And then you can use that global variable in all of the other boards Okay. In all of the other panels. So if I you know, maybe we do this.
This is like global values up here. Great. And then we have this, you know, dynamic portion. So what do we want to know here? Well, we want to know I I don't know.
Like, what about are we doing we're not doing per turns. We're not storing turns too. Maybe we just do what do you think?
Speaker 1: There's only one other thing that I thought would be really fun, but I think that the logic is gonna take too long. No. And it I mean, not gonna be related to this panel. I think it's I I'd be very interested to pull in some custom that that the the actual gift for the game. I think there's something interesting there to be able to pull in.
Speaker 0: I think we could do that. I think we could do that. Yeah.
Speaker 1: That's an ambitious it's an ambitious goal. You wanna try?
Speaker 0: Yeah. Go on then. Right. Tell me how to get the tell me how to get the URL. Go to your game.
I'm I'm going. Got it. Alright. Go to view
Speaker 1: as GIF, and then it'll give you the format
Speaker 0: that you I will only click it when you say it correctly.
Speaker 1: GIF
Speaker 0: GIF. You may. GIF. Okay. And there's a URL and that's the game ID.
Speaker 1: Grab that,
Speaker 0: And that's that ID is movable. Right? Like, that ID is the ID of the game.
Speaker 1: So
Speaker 0: you can buy back
Speaker 1: and game by game.
Speaker 0: Yeah. So let's try our hand.
Speaker 1: We need to do a custom panel.
Speaker 0: That is that is definitely what we need to do. Yeah. But, right. So what are we gonna do here? We're gonna go up one layer.
We're gonna go MPX create directus extension at latest. Whoops. Extension. Helps when you type it correctly. Right.
What we're gonna do, we are oh, you're you're putting me on the spot here. I'm not overly confident. And viewers, if we don't get it done, we're not gonna get it done. But let's call let's consider this a stretch goal. I also haven't used these global variable values too, too much.
While that's kind of doing its thing whoops. Docs, Data Studio app. I think it might be over in the user guide, insights, panels, global relational value here. Oh, and then in other in other, ones, you can use the variable key like in other panels. That's interesting.
Anyway, we want a panel, director's extension, snake panel. JavaScript install dependencies. Okeydokey.
Speaker 1: I'm glad I gave you this challenge, and I am not the one having to implement it. This seems right. This seems good.
Speaker 0: It all seems good with the world. Alright. So we have the panel here. We have our panel dot view. It has been a while since I built an extension.
So here we have all of the configs. We'll call this snake GIF snake GIF. Sure. We could do loads of configuration. We're not gonna do that.
First thing we're gonna test is if we just have a if we just have a string like we manually insert it to start, can we can we play? Can we play it back? So in the panel, we have this text here, and I think all we would need to do, there's a bit of an assumption in here, is we're gonna paste in this URL and we're going to interpolate here the text, right? That feels right?
Speaker 1: It feels very good.
Speaker 0: Npm run dev. Sorry. Npm. I'm I'm not even in the Npm run build. I wasn't right running the right command or in the right location.
And then we will I think what I'll do is I'll can I hang on? Kinda wanna open them side by side. There we go. And then I wanna just kill that, restart it. Once again, fixed as of, like, yesterday's release.
I just haven't got around to upgrading this one yet. Open up our panel, and I'm hoping snake GIF. Hey. We insert an ID here. Let's go get an ID.
This one, 72 turns. And so I I can't believe it might actually why did I say that?
Speaker 1: You've jinxed it now,
Speaker 0: but it's okay. Here. Hit save. Okay. Save.
Speaker 1: And
Speaker 0: oh. Don't know, mate.
Speaker 1: Happening. Oh,
Speaker 0: I have my dev tools kind of detached. Content security policy.
Speaker 1: So this might actually be cross it might be course with the dev side
Speaker 0: platform. Which is which is, the ilk of what this is. How do I solve it? How do I solve it? It's a GIF.
It's a GIF at a location. Can we import it maybe? Can we import it? And then how would we import it? Let's take care of let's let's consult the docs.
So we have in here extensions, developing extensions, extension services, accessing files, import a file. This is inside of an API extension how we would import a file. We would instantiate a file service. We have an asset key, which has the URL and a file object, which contains additional metadata, and then we read it in order to to return it. Excuse me.
So let's have a think about the right way to play this. We could press a button. It'll import it and then play it back. It's a good idea. Let's try
Speaker 1: that. That seems easy. Let's
Speaker 0: try that.
Speaker 1: I say this. I say this.
Speaker 0: Easy as a That sounds easy. That sounds easy.
Speaker 1: Kevin problem. Sounds like an Android watch, and Kevin solves this tactical challenge.
Speaker 0: Alright. Alright. Alright. So let's just keep this easy for a moment. Let's let's stitch this off, and let's create a new bundle.
And I'll call this one directus extension, and I'll just call it, Battlesnake Viewer and install dependencies. So that's creating a bundle. In that bundle, we'll have an endpoint and we will have, and we'll have an endpoint and a and a panel. So we'll CD into the battlesnake viewer, and we'll go npm run add. And the first thing we'll add is actually the endpoint.
We can just call this one importer. JavaScript. And now inside of Battlesnake Viewer, source importer index dot j. So this is again, our custom route as mentioned earlier. Now inside of our existing one, we've actually got this is snake GIF.
We have our actual snake here. We're gonna we're gonna just kind of nab this structure for a moment, inside of the importer here. We'll remove these actual things here, like so. Okay. So we'll call this one importer.
Right. So when we hit importer, what we're gonna do, and we're gonna do a post request here, is we are going to access files. I'm gonna copy all of this. Here. We'll see what we can do.
Oops. Okay. Router dot post. We will create a files service. Now I actually think, the docs do talk about importing that.
So let's just take a little look at the top here. Yes. Okay. So in the endpoint itself, we have a router and a context. Awesome.
Let's do a let's do a little more side by side jobby here. Right. So we have router and we have context. And up here, we're going to grab these values out. Then inside of router.post/cool, we will create a new file service.
We will look at rec. So file.url is going to be req.body. URL. Cool. And the file object itself, how much of this must we pass?
Let's look at the file object. API reference files. File object looks like this. How much of this must we pass? I don't think any.
I don't think we need any of this. So I think we'll remove that. So we have our URL. Then we will import it. It will come back.
We will then have the asset key, and we'll return the data. So if all goes well, npm run build, This is our importer, docker compose up. And I think what we wanna do here is test whether this endpoint works. Now I still have ngrok running. I don't still have it running.
I've been running it since the beginning of this, this session. And I have hopscotch here, which is similar to Postman. It's just like an HTTP Explorer. So here, if I go importer post and inside of the let's just hit send there. Okay.
Timing out, timing out, timing out. There is an error, invalid URL, input object object. Okay. Let's just cancel that. Let's set a body.
Let's set this to be application JSON. There we are. And what we want in here is a URL of and then this exporter battle sync. So hopefully this will return the ID of a new item in our directors project. Okay?
It's still going. So not feeling good about that. Invalid URL. Is it, though?
Speaker 1: Is it? Apparent I mean, no.
Speaker 0: No. No. Object object. Import 1. So it was struggling with import 1.
So let's let's take another look at this. So it was struggling here with the file service dot import 1. Req dot body dot URL. Now to be fair, we never once took a moment to check whether it was gonna be req dot body. So let's just res.sendokay for a moment and that's console.log req.body.
There was a bit of an assumption in there. So we will rebuild that and then we will restart that. Okay. And we will send that and we should hopefully get an okay back. We did.
So let's take a look. So inside of req.body, yeah, dot URL. What's going on? Is this a URL we can hit? Oops.
Yeah. Should be. Yeah. Sure. And let's just test that there's nothing funny going on with, like, access here.
If I go to our file library and import, this is the RF for importing and hit import. The GIF is there. So there is nothing wrong with that. So we're just doing that via API instead. So fair enough.
Fair enough. So we have the URL. We have some accountability, which I think we might just be able to bundle some
Speaker 1: for now.
Speaker 0: Get rid of it for now. Let's do npm run build and restart the container. Go to hopscotch and send this.
Speaker 1: Okay. Great.
Speaker 0: And this time, we've got a URL, which means there was nothing wrong. So let's just do one step at a time. That's console dot log the asset key. Yep. Rebuild.
I'm determined to get this to work now.
Speaker 1: We are gonna do it. We have we have the Cool. Technology.
Speaker 0: We have the ability. That should still just return an okay, but it hasn't. Why? Code error invalid URL. It gives me it gives me it gives me the docs might not be right, which is possible.
So as much as I wanna just be like, but that's what it says to do. Let's take a moment and let's actually, if we go to accessing if if let's find let's find the docs for this and let's just step through because all of the services are just in the code base. It's not my preferred way, of course, to be looking at stuff. Let's take let's take a little look. What we care about here is import 1, and it does expect an import URL and a body partial.
So maybe maybe we did need to pass in, could not fetch from URL. The error said invalid URL out of interest. So maybe yeah. Because what it does is it grabs the raw file and then uploads it. Maybe we need to we must pass in this body.
Just a thought. And that was the partial body objects. And maybe I mean, that's a partial body object. Maybe there's something else we must pass in this at the time of creation. Let's, let's hit send.
Speaker 1: Come on. Okay. Okay. Okay.
Speaker 0: In fact, again, it's taken that long. Yeah. No. Input object object code invalid URL. That invalid URL.
Couldn't fetch file from URL object object. Isn't that interesting? Because the URL to me is a is a string. Right? Yep.
And found that URL. But this time, before all of this, I logged service external file is unavailable. Couldn't fetch file from URL object object, but it isn't. It is a string, and this is the string. Fascinating.
I mean, let let's just, you know, I've been a bit cheeky here. Maybe we, go back to accessing files, and I do actually create it with this accountability object here. Yep. I'll take that. I'm pretty sure I know it's not needed.
I know it's not needed. But that the error isn't saying something else is missing. You've triggered an unhandled rejection. You may okay. Yeah.
Fine. Okay. True. So if we go if we try around this, right, and then we catch an error. We console dot log the error.
And we send this.
Speaker 1: And
Speaker 0: We are expecting an error, which is fine. We've got 503 service unavailable. Why? Couldn't fetch file from URL. This is what's troubling me.
Object object. Let's take a let's just take another look here. So import 1. I think I worked it out.
Speaker 1: No. That was quick. Goodness.
Speaker 0: It is an opt I did pass in an object, didn't I? But that's not what's being passed in in the in the reference. The import URL is a string and the body, which is a partial file that should have been passed in as a string. All you need to do, Kevin, is read. Read the docs.
So read
Speaker 1: the There's another person here, and we still didn't get there.
Speaker 0: Yeah. Okay. Asset key. We got an asset key that time. Success.
There should now be a file in the file library. Right? Correct. So we are loading it in. We are loading it in.
Title, description, tags, file name disk, all grand, but
Speaker 1: Beautiful.
Speaker 0: What we also want to do is this. We want to, YOLO. I can't be bothered with all this. We'll just there we are. We'll take the asset key, then we'll read the new asset, and we'll return the data.
So what we'll get back in in response to this is it will always import. So that's not ideal because if it already exists, we don't necessarily want to reimport it every time. But for the sake of this little demo, I think it's fine. Then we'll get the ID back. So now if I rebuild this and we run this again oh, it was still spinning up.
What we should get back is the new file object indeed, including the ID. And the nice thing about this is, if I go to access control, public, and I turn on directors files actually, I don't even need to do that. If I go to httpsphzedm.ngrok.io, right, slash assets slash ID will get a you don't have permission to access this error. Right? But what I can do is go to the user, generate a token.
Speaker 1: Okay.
Speaker 0: Generate the token, save it, and I pin that to the URL. Nice. Access token equals. And there's our Hey. Merge directly, which is pretty which is pretty cool.
That's very cool. To keep this easier, though, I'm actually going to change the permissions of the files collection. So then you net we never need to pass in, pass in the key. So public system collections, directors files, all access on. Great.
So now that's public. So now we've done the endpoint. The job is to create the panel. So what we'll do here is npm run add.
Speaker 1: That should be pretty easy, I think. I say that. That's a dangerous
Speaker 0: Easy for you to say.
Speaker 1: He does it. I know. It's very easy for I I think you've got this, Kevin. I trust I believe. I trust in Bodie.
Next week, when I'm when I'm driving again, things are gonna go terribly awry, and you're just gonna kick back and and relax and
Speaker 0: enjoy the experience. Oh, yeah. I know. Viewer. What did we call the previous attempt to an extension here?
Snake GIF, find viewer, viewer. This is my custom panel. Now there is some text. Now what we need to do, I think what we'll do is we'll remove the options entirely, right, because we don't want any options. It'll be a standard panel.
And inside of that panel, we are going to have button button. We no longer have text. That's not a prop that's being passed in anymore. We just deleted it. So what we'll do is we'll do a v button.
No. We won't. We'll do a v let's talk about what all these v things are for a moment. Back in our docs here, back in our docs, we have, resources for building extensions, components. And we have this components playground.
These are all of the directors components that are inside of the Data Studio are made available to extensions. So we have here v button, and this is the director's button inside of the director's app. So we we have an immediate availability of this v button component, which is, yeah, pretty, pretty nice. So we can access that straight away. So we can go v.
I did just notice it was v button like this and we'll call this, not v. I keep going back to the button. Sorry. We're looking for
Speaker 1: No. That was right.
Speaker 0: No. Yeah. But we want the input because we want to put in the ID. Yes. Yes.
Yes. Because we'll keep it easy. We'll we'll put, like, right now, manual ID of a game hit go
Speaker 1: Yeah.
Speaker 0: Rather than, Automating it. Yes. Pick pick it from a drop down because we're running we're running out of time. So v input to v model. So, what are we gonna do here?
Export default. We're gonna do data return, and we'll call this ID. Like so. Then inside of here, we'll put in the v input with a model of ID. Then we'll say on key up dot enter fetch, game, GIF, get GIF, fetch, git.
Cool. And we will add and this is not really an ideal way of of writing these, of course, but methods and we'll call this fetch GIF. Yeah. And we'll just console dot log or you know what? We'll alert with the this dot ID value.
That's what we'll do for now just to test all of that works before we go too far. So we'll go in here. Oh, I need to sorry. Npm run build was the command there. Oh, error.
Speaker 1: Come on. What are you doing, error?
Speaker 0: What are you doing? After component panel component, we're missing a comma. Okay. NPM run build. Lovely.
And we'll restart that. Also lovely. So let's refresh. Let's refresh. Just took a hot minute there.
Insights. New panel. Viewer. No options available. Right?
Because nothing happens to the panel itself. This, wonderful, is our panel and we'll say we'll save it. Hey, enter. Great. So this means we are able to do something with that.
What we're gonna do with it is we're going to go back to our docs. We're gonna look at our composables for app extensions, which is what this is. I'm gonna just close out everything else for now. And we're gonna use API. And what this will do is it will allow us to make API calls, to our endpoints that are already authenticated.
Though, to be fair, I believe this is a completely public endpoint, so it shouldn't matter much. But still, we'll do it as as it should be done. So what we're gonna do here is we are going to import use API. We are going to use this composable, like so, and then we can make our endpoints like and then we can make our calls like this. So we will make this async.
And what we're gonna do is we're gonna post to slash importer/importer. And we have response dot data here. So all we're going to do is console dot log response dot data. Let's build. And this is all basically to get around that that security issue.
Speaker 1: Yeah. That course issue. I love that there's always just like a 6 step workaround to deal with course. Thank you.
Speaker 0: As opposed to just dealing with it head on. Yeah. So, we refreshed it. This is great. What's happened to my text box?
What's happened to the text box? Is it no, but it's not. Where's my text box? Also, what's happened to this one? What's happening?
Not seeing any errors. The API could not be found. Couldn't load extensions. Okeydokey. Have I missed a step?
Have I rushed it again? Have I rushed a little bit? Oh, script setup. So we'll be using, we have to move this to script setup. And let's just take a quick look at how this I need to refactor this.
Of course, we do, but that is fine. Async fetch GIF should just be able to, for the most part, come up here. We'll say, const fetch GIF equals a sync function like so. Then is that legit? Yeah.
That is legit. And then so that's the methods. Then we have the data, which is the ID. So we will be import import ref from view. We'll create a new variable for that.
We'll go const ID equals ref. Great. So we've handled that. Then we have props. And I believe the way to do props here is we say define props.
Can we do this? I think we can do this. And all we care about here is show header, which to be honest, I do not care about. In theory, I've just refactored all of that. Let's take one more quick look, fetch GIF.
So we have this dot ID. So we don't need that anymore. It'd be ID dot value, but we're not gonna alert it anyway. Kevin, watching
Speaker 1: your work is just fantastic, by the way. I love that I gave you this challenge and, like, everything has gone wrong. Okay.
Speaker 0: This is this is an Axios instance. I don't quite remember how it bloody works. So I think what we do here is we do, and I need to check, axios post. Oh, we do we literally just pass in the whole object as the second. Great.
So in here, we're gonna do URL equals ID dot value. Okay. And then we have response dot data. Cool. So we'll console mock that.
Alright. Let's rebuild that. Is it gonna complain this time? No, it bloody isn't. Docker Compose up.
You give me a right workout here. Let's refresh, and we should now have access to use API. Now none of it's loading. Now it is. And there's our text box.
Let's open up our dev tools. Of course, it didn't like the URL that was before. That was this panel here. Just testing that out. So in here Can I grab a screen?
Pop it off at the side. Over here, yeah, we need a game ID. So we can do that here. Game ID. So we're not done yet because we're not gonna display it yet.
If we hit enter, what's happened?
Speaker 1: Refused refused to load the images.
Speaker 0: No. No. No. That that's the old one. That's the old one.
In fact, if I just remove that whole thing, we'll be better off for it. Let's refresh. See no errors. Let's paste that and let's hit enter. Nothing's happening.
Why? Fetch GIF. Let's make sure this is ever running. You know what? This is all cute and all this.
Let's just, just in case it's some silly async fetch gift, just in case it's some silly, async function, just in case it's something silly in the way that oh, I think it might have said invalid URL. I think it might. Do you wanna know why invalid URL? Because it isn't the full URL. We're just posting the ID.
Oh, that's right. Yes. So what we'll do on the importer package is we're actually going to, we're gonna import req.body.url. Cool. So we're gonna make URL.
Where is it? Here. Exported.battlesnake.comporter.battlesnake.games. And then here, req.body.idgamegame, maybe we call it. And in here, we pass in the earth.
So now it needs a game. That's the value we need to pass going back to our panel. This is going to be game ID dot value. Let's rebuild the extension. Okay.
Oh, yeah. We'll refresh. I do understand our network error showing there. Now we'll paste in the ID of the game. We'll hit enter.
Oh, you know what we didn't do? We didn't open up dev tools. Let's skip to console. Wicked. Okay.
We now have an ID of a directus I like, a directus asset in the ID. So all we need is the ID here. So this is the ID of the game, game ID, game ID, and then an image in here and the source of that image is going to be, how is this going to work? We need to just have a quick think about this. I'm gonna try something out.
I think if we go /assets slash and then we need a value in here. So in here, we're gonna go image ID, and it's gonna be a ref and image ID dot value equals response dot data dot ID. Right? So Yep. Now this has the value in it of the ID, image ID.
First thing we'll do here is just a quick v if image ID. We only wanna show it when that's the ID, and then here is going to be image ID. Let's see if that works. I'm not a 100% sure it will, but should do.
Speaker 1: I have faith oh, yep. There we go.
Speaker 0: I don't. Let's refresh.
Speaker 1: 1 of us has got 2.
Speaker 0: Great. ID is not defined. Why? Because ID dot value is gonna be game ID dot value. I'm feeling the heat, but I'm having a blast.
Speaker 1: I know. I was gonna say I feel like I gave you, like, the small challenge that became the much larger Yeah.
Speaker 0: But not not 145 in or whenever it was. That's not really the time to do it. But if we wait just a moment, nothing happens. Why? Console.
Assets undefined 4 3. Fine. Let's figure out oh, image ID. Complete typo there.
Speaker 1: Remember this, Kevin. Remember this as the challenge comes next week.
Speaker 0: I'm having I'm having a blast. I really enjoyed last week, last episode. And now
Speaker 1: we have Hey. What is I? Kevin. Kevin. Kevin.
Kevin.
Speaker 0: In an ideal world, you can obviously select from your existing games. It will handle all the logic for you. I think we're firmly out of time to do that this time around. Next time we and next time we won't be touching insights. But everyone, I hope you've had a wicked time.
Any final remarks, Andrew?
Speaker 1: Thank you, Kevin, for taking on the challenge. Thank you all for watching. And be sure to tune in for our next episode of
Speaker 0: Ready.
Speaker 1: Ready. Set. Set. Battlesnake. Battlesnake.
Speaker 0: Nat, when you edit this, can you just time it so we get that right? Thank you. Bye.