DotNet Core REST API with Cosmos DB Database

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone its Andy from Julho computing just built a project with asp.net core and the cosmos DB through Microsoft's as your services cloud services and I did that because I got a grant for a not-for-profit from Microsoft for a lot of services and the cosmos DB came out at least according their modeling quite a bit cheaper than a regular relational database even a post gray or a MySQL so I picked that so I could purchase other other bells and whistles and I had to kind of feel my way through there were some tutorials online which covered a lot of it but I had to kind of peck around and and pick and choose and put together a rest setup that that worked well for me so if you died I'd share it and and put it out here on YouTube in case it's it's useful for anybody cosmos DB is a they call it a distributed multi model database and what that means is it's distributed and replicated all over the globe if you want to so that your your customers can get get data faster and the multi model is kind of neat basically their API is where you can interact with this no sequel database in ways that are familiar to you so you can interact with it as if it's a MongoDB database you can interact with it as if it's a graph QL and for the purposes of this tutorial we are going to use to be the sequel API one quick note before we dive into it you'll notice in this video I have a little trouble with a debug versus release configuration that did turn out to be just an issue with an upgrade on Visual Studio for Mac and once I rebooted my computer it all worked fine so I tear my hair out a little bit fretting over that in this video but it did all work out fine it's just a little bug not a good bug because it caused me to write records to my my development database instead of my production it does seem to be straightened out now so without further ado there we go all right so first of all I figured in the time it took me to get all these resources together and put this video out here on on the web that Microsoft would probably come along and do that documentation themselves and sure enough they did the use of that there was a under the bill the console app there was dotnet and there was a dotnet core example or a tutorial here this is under the cosmos DB tutorial page and then under web app they just had dotnet and there was no dotnet core so you could kind of get the feel for how dotnet core worked with the console apt but it wasn't really clear how you would create a connection string that or a connection to the database that would carry through to two different controllers so they did fill that gap but as I go through and I look at this this is a little bit more complicated than I want from from my scenario there's more happening here it's it's an MVC page more happening here that I need and I decided that you know I still kind of like the more simple solution that I came up with that it's just a REST API so figure will throw this out on the web if it's helpful to you great if nothing else it'll be a nice video that I can watch when I need to do this again and I forgotten how I did it in the first place so I'm gonna go ahead and start a visual studio project so I am using Visual Studio for Mac which is gonna have slightly simplified options compared to the the windows option but essentially what we're looking for is dotnet core we're looking for an API and you'll see I think the description is is copied and pasted right from the the Windows version which I I do have and should have checked but this doesn't blocks you into just being a REST API this can can convert into an MVC application down the road it's the same architecture but it's it's just a little simpler template so you can pick that or the equivalent of that on Visual Studio for for Windows it's just the API dotnet core API you target framework I'm gonna pick 2.2 3.0 we'll be out shortly and if that's available to you might as well go ahead and pick that or whatever the latest version is and let's just call this cosmos API and I think I actually might have created one called that already we'll just God v2 for my purposes I am NOT going to use yet here I really have no intent of saving this this is just gonna be a throw away from me but if you're creating a project that you do want to use get for you can go ahead and leave that checked all right and while that sets up there we go shouldn't take too long the other thing we need to do is if you haven't done this already is you need to have it as your account so you need to need to sign up this is uh it's it's you can get one with a credit so if you don't have an azure account already you can get a three hundred dollar credit to play but do keep in mind that if you create a database here that the you know your expenses are running so if you've if you've got the three hundred dollar credit it's gonna draw that down if you don't then you will be paying for it so you definitely want to be you want to if you create this database you want to take it right out when you're done but we are looking for a cosmos database which is over here on the left I'm staring right in the face there it is so I've got one already but if you do add one and this is all included in this document so the fact that they created this is great because they walk through the steps here of how to create an account how to create a database and so I'm just gonna highlight real quick that the one thing you really need to be sure of for this tutorial to work obviously the other options are your own to choose but you know when they say like I said when I said that they say a distributed multi model database the multi model is that there's an API different API you can access this win so you can access as if it's a MongoDB database Cassandra you can use graph QL through gremlin and we're gonna use sequel and we're going to use a sequel API to access a no sequel database which seems a little funny but you know in this case we see that means you can use normal sequel query language you can also use link which in my mind when I have a series of text acts on the table and c-sharp is in the mix c-sharp gets a big boost because of link if I'm using lots of data because link is just really the best the best way to sort through data that I've ever encountered I really enjoy using it so make sure you get course equals selected there and he will create your database and then you will have one underneath your cosmos DB tab and I've got one it's called falling snow all right so when that set up we go back here to Visual Studio our project is created if you would like you can run it just to make sure it will work it's fairly fast so let's go ahead and do that and it will come up on localhost 5001 that just gives you an opportunity to show you that everything is working there's a base API that just feeds you value on value to our base controller all right so next thing we to do is to go to the app settings and set it up to connect to our database so under properties or excuse me under app settings right here this is essentially our config file and we've got to it we've got a config file here we've got a config file for development I know I said I assume some knowledge here on C sharp and dotnet course this is probably just a basic for you but just to kind of cover what I'm doing and walk you through all the steps of it so I don't lose anybody so this is for production here this app settings and then development is here so when we're just running in our debug mode it will read out of this config file and we're running in release mode it will really it'll read out of the app settings JSON and in Visual Studio you can change that so you can be in release or debug mode and what I like to do here is go ahead and setup the connection the items that we need to connect to the database so I'm gonna create a config value called cosmos DB and then I'm going to give it a URL property and then I'm going to give it a primary key property and these are gonna be what I refer back to to make the connection to the database and so these values are gonna come from your panel your your portal on Azure they are in your database so you go click into your database and there is a tab over here called keys and you are given a URL and a primary key obviously I'm gonna hit refresh and change this one I'm done but right now this is my primary key for this database and this is my url up at the top here so we're gonna grab this URL we're gonna pull it over here we're gonna paste it into there so that's where we will point for the database we will grab this primary key as well and we will copy that into here alright I would add one more property and it's actually gonna be outside of the cosmos DB object but we're gonna add one called database ID and this is optional you don't need to do this but the project I'm in but so you could call this whatever you want but basically this is going to be the name of your database or the overall high level collection in your in your no sequel database you can have several of these which we're gonna use here because this is gonna be the production file config file when I am in release mode I want it writing to this Island line underscore prod production database now we're gonna go to the development actually I'm gonna copy this not that last part this part here so I'm going to copy the cosmos DB object the database object I'm gonna take it over to the development connection our app settings and outside of logging put a comment and we're gonna paste this in and then we're gonna change this underscore to death so that make sure it goes it goes to the same database instance it uses the same primary key but if I'm running in debug mode it is going to write to the development collection and if I'm in release mode it's going to use this config file and it's gonna write to the fraud collection not something you have to do you could put the same database ID in there both times you don't need to have the database ID and it can in a config file you can have it hard-coded in your controllers but this is the way I set it up so that we can have a dev data base in a production database okay so that's it for these config files and I do want to save them alright and now we need to get some dependencies or some some libraries here so under dependencies under new get I'm gonna actually go up here to project add new get packages because it's been locking up when I right-click alright what a couple packages here we want um I think you can just put Cosmos DB not really okay we're gonna put document DB dot core alright so this is the one we want Microsoft that is your document DB core and you see right over here in the description this client library enables the client applications targeting dotnet core to connect to as you cosmos DB by the document DB sequel API and that's exactly what we want so we're gonna go ahead and add that package and while we're doing that we will I think we can just go ahead and start stacking another package on there we're gonna add Newton soft which is the second one that comes up you might want to query it you might already have it it looks like in some of the latest releases Visual Studio they're just including it right in there but if case you don't just take that out there that's the spelled Newton wrong okay we go ahead and add that and that just assists is it really really handy library four and there are all kinds of dependencies so we'll just go ahead and accept that really handy library for converting objects to JSON and JSON back to objects really critical for your arrest operations okay next thing we need to do and set up is we need to change our start up so that our database connection happens there and really just one line to add here so right after services you're at NBC right here set compatibility version we want to add another line right there and we just want to add a singleton service that makes that connection to the database and we only want to do that once and if you are freaked out by not having an autocomplete I know we've all gotten used to it I mean you could just type along with me but if you want it to autocomplete you can go ahead and do your import first and so we want to add as your documents and also Microsoft dot as your documents dot client there we go and now we've imported what we need here and we will get singleton alright and we want a type I application builder we're just gonna nickname that app and then the second parameter is I hosting environment oops I'm sorry I got on the wrong line there we go forget that I document client is what we want to create and we want to spell that correctly all right and here's a little lamb to query for here and lambda syntax alright so there we are passing at the URL and we are this notion right here this is just pulling it out of our config file and then we are gonna pass it the key these are commands are just long enough they go slightly awkward they off the screen but not quite long enough to to really wrap and have it look good so hopefully you can see all that's kind of long but this is passing the URL and this is passing the primary key to the singleton and then they document or the connection to the database is made once on startup and then you are all set to access it through a controller all right so all you need in the startup is to add that one line and then of course you have to do these two imports here which you can do first or you can do afterwards doing first will give you intellisense when you type all right so now we're ready to create a model so I'm gonna add a folder we're just gonna click right click here in the grey space I want to add this out at the root level we're gonna add a folder called models and we're just gonna create a simple generic hackneyed items model so I'm gonna say new file we're gonna add new file and we're just gonna call this item dot CS it's just gonna be an empty class you can probably find a template for an object but we'll just go ahead and start with a generic class and make it into our object so we need to take this constructor out of here because we don't need this so our public class is gonna be an item and we need to add some properties to it and we'll just go ahead and add and we'll capitalize those because that excuse me is what what's these sharp likes to see will add an ID will add a name these properties are entirely up to you this is your model of your object and we'll just create a description as well some people look at on the we go we had to get her and set her to each one of those like I said we capitalized that if we lowercase it yeah you get a little underscore there this is not going to cause you any real issues but you are gonna get that underscore that that's not a typical naming convention for C sharp objects you want your properties to be uppercase alright so there's our object right there alright so now we're ready to add a controller now we've got a values controller here this is the one that comes out of the box and it basically just returns a JSON object or string with the value 1 value 2 and that is what we saw here we're gonna add our own controller and so we'll go add we'll go to new file there are templates you can use to create it I am just going to do empty class I'm not sure if there's a great template here on yeah that gets them but look a little different on Microsoft Visual Studio for Windows so you know if you find a template that works for you great I'm just gonna go ahead and start from scratch cuz I think that that's uh we really don't need much here we'll just go ahead and create our own controller so the one thing we need to do they always make sure that it's named properly so we're gonna call it item controller and the proper part is you just need to have controller here at the last part of it alright make this controller I'm just gonna use the base controller which is just the very basic function so this is gonna extend controller base and a lot of this stuff as you type it you're gonna get an underscore that you don't have it available and you just want to import these items so first we're gonna import MBC which has that controller base which we're gonna extend off of and that just gives us some basic rest functions there other controller so that you can extend with more more features this one just has the base which is really all we want we don't want extra features getting in our way that we don't need I prefer to go ahead up top here and go ahead and put the route or route whichever part of the country you're from right up here at the top and I'm gonna do API controller which actually is the default so I think technically this is not necessary but I just like to have that right up on top of the controllers just so it's very obvious when you open this where it goes if you this is gonna take the controller name so it's going to be API slash item and it's gonna drop the controller from the name of the class you could hard code something in here you could put API slash my items and then that would map to this controller you could put that in the URL and it wouldn't matter but by doing this controller in brackets it just uses the name so we could just put item here if we wanted to as well and then we're just gonna put API controller up here and that makes this a controller that is mapped to API slash item and we're gonna need a few variables at the top we are gonna need a private read-only I document client and we're gonna name that understood core document client and we are gonna go ahead and import as your documents which we need to do there I'll get rid of that red line and that is gonna end up being our connection we are gonna have a read-only string so this is like final if you're coming from another language or you only are const database it just makes it so you can't change it you said it once in the constructor constructor and then the value is static from there I'm gonna have database ID we are gonna have a collection ID and then we're gonna bring in our configuration which allows us to access our config files and we are going to only get we have no desire to set in there that should be a capital C and then we will need to import the extensions Microsoft extensions that configuration and then we can access the config file alright so in our constructor we are gonna want to pass i document clients Wow that was a nice autofill i configuration and we're gonna want to set our underscore document clients equal to the document client we are passed in the constructor and that is going to be what we use to access the database that's our connection it fires up when the the controller is created it connects and then we've got this document client is our can our route back to that conduct confection connection to that database and we want our configuration figuration to be equal to the configuration passed to us in the constructor and the database ID we want to go ahead and get that from configuration database ID so this is gonna go ahead and get the database ID from our configuration file so again if we're in debug it's gonna get the development if we're in production just gonna get the if we release it's gonna get the production database and then our collection ID I'm gonna go ahead and just set here at the top items and this is what I like about this setup is that each controller can be set up to a different collection which you're gonna have to do some modifications to do on the the current example that they have on the Microsoft documentation here so this provides a way to do that every controller I only plan to have a handful of controllers here every controller will change this so that each controller is controlling a separate collection in the database and then we're gonna wanna build the collection which is a function we have not yet built and it's gonna be a sync so we're gonna want to wait on it and then we will go ahead and do that function and task is async so we're gonna need to import using systems threading tasks and no go ahead and put that async await to use right now so there's our document client that's our connection and we're gonna use a command called create database if not exists async nice succinct title there but very descriptive so we will create a new database with the ID equal to our database ID so this if it will check and see if the database exists where you're grabbing the database ID from a config file if it exists already it will do nothing if it doesn't exist it will create it so every time this controller is constructed it'll go and check so if we do the debugs debug and we run it the first time it creates a database and then we're running and develop a development for a long time first time we go to release we go ahead and run this and it will create the database in the production environment for us for the first time and again this always runs and if it's there it just leaves it alone alright so once the database is built then we want to build the collection so we will do another wait and that is also an asynchronous test that's why we use a wait and we want the database to build first then we want it to wait for the for the database to be done before we put the collection in it so we are gonna go to create document collection if not exist async another nice nice short title for us and we want to create a URI Factory which just basically creates our reference you won't get a drop down yet because we need to import the namespace for that but go ahead and create database you are I and at the database ID so this gives it the location and so we'll go to URI Factory we will import using Microsoft as your Documents client and that will make that red line go away perhaps so nope I am actually gonna wrap this argument here we've got one more parameter to provide you know that is new document collection and the ID equals the collection ID so we actually have to create the collection close that - really so there we go alright so this key the database URI directs it where to create the collection and if the collection doesn't exist it will create it at that space and that is all we need for that that build collection function so this is something at the top here all of this is gonna run every time check for the database check for the collection and if it doesn't exist it's going to create it and the configuration here we've got our database from our config file and then we've got collection ID basically hard-coded here at the top for the whole controller so that if you set up a second controller you use all of this except that you're gonna want to change the collection ID to another so you might be able to extract this if you're building multiple controllers to kind of make it simple but for the number of controllers I'm building this is this is plenty transparent at the top I like that and even though I might repeat myself a little bit it's not a whole lot of code to to repeat so then we can get into the actual crud operations so we're gonna start with a get a couple of gets so this will get everything and we are gonna start using a link here a little bit which we have it imported yet but we're gonna bring that in in a second and that will make our squiggly red lines go away so let's take care of that now so I queryable as part of links and we want to import using system link and our item see if it gives us the option here we need to import our model's namespace yep it gives us the option right here so we just need to bring in the namespace for the models so that we can use our item model and we're gonna use our document client as our connection to our our database just wanna use that a lot here we want to create a document query of a type and that type is going to be item that's what we're looking to get back there's our URI Factory which is going to create our reference create document collection you our eyes when we're looking for here and the URI is gonna be gotten from passing at the database ID and the collection IDs it knows what it's getting and we do need one more option here so before that last paragraph this is we're gonna Pat as past the feed options which may actually be optional but you can certainly just include them and do this for later or you can add an option in there so if you only want to get a certain number back so you might not want to just have a completely open-ended query there's some options you can pass here one is max item count so let's just say you know when I say get all I mean just give us 20 top 20 all right and our next HTTP to get pretty common in a restful controller is gonna be get one so we're looking for an aquaria Belov type item and that's also again and cursor troubles here all right create document query same as before and also an item we also want to URI Factory here so this is kind of a common pattern this is basically we could have copied and pasted if we wanted to alright so same thing is below here but the semi wants new feed options just like before but this time I am gonna say max item count equals one now I'm in our intent here is to only grab one by the ID but if we somehow grab somehow the IDS are duplicate we've kind of slipped up and allowed that to happen in our database this will only grab one and so we can handle that scenario and this is where we start using link this is a good thing gets me excited anyway alright i dot ID equals alright so we're only gonna grab the record that equals the idea that we get we pass and we can pass that ID up here so we say get we want to get string ID and our HTTP GET HTTP GET we also want to to tag that and say yes we're gonna get a parameter called ID from there these are basically the same HTTP GET and this HTTP GET except that we have also decorated that to let it know that it's gonna get an ID parameter from the query string and put it down here in the function as well and then we add the where clause at the end so let's do a post and then we can do a little bit of a test just to make sure we haven't totally messed any of this up all right so we do a put request and not a per request to put request and then same things above we're gonna want to pass it an ID nope sorry ahead to put we don't want to do put yet let's do post post request first so that we can actually have something to to update later so let's add async task I action result and there's a number of ways to get information back from a post in dotnet but we're gonna use an I action result which is just going to send us an HTTP code that lets us know that everything is okay a 200 code if it works results I need to capitalize a ok so I'm gonna do a response here so I'm gonna set a VAR a variable called response and it's going to await on the document client and we are gonna create a document async and we're gonna use that old familiar URI factory so the patterns start to hopefully are starting to become apparent here the URI Factory is basically just to create a reference in the database and we are going to create document URI from a database ID a collection ID and we are passing it an item and create documents and that should be create document collection URI that'll make that go away all right and then we need to return something so I am going to return ok so obviously you really probably want to have some try-catch block return something here if there's an error but just in a simple setup it posts that information adds it to the database and returns and ok this response is underscored because we're not doing anything with it and it is it needed you could take this out entirely but I just wanted to show you how you could do that and there's all kinds of different things you can get from the response object so if you want to pass that information or want to use that information it's there so I'm gonna just go ahead and leave that there but you could definitely take this out and just do if you're not interested in any of the information you can just do a wait all right so that gives us a post and so now I'm gonna get a postman let's actually test to make sure that some of this is working so we'll go ahead and hit go that will bring up I'm gonna just close this because it will bring up a new instance of the of the tab and once it's up and running we're gonna bring over postman there cuz it it defaults to API values so we could change that to not I know this but we called it item and that will connect to our database and pull us all awry damn objects which is none right now so we can add some so we can start to get some more meaningful tests so I'm gonna go over here and we got a postman and so what we want to do is create a post we want to create it to localhost at five thousand one slash API slash item we want to go to headers and make sure we have content type application JSON setup we need that one and in our body we are going to create an item so we'll create I'm gonna go to raw in the body and we'll create this object it's got a name of my item and we'll call it my item description under description and I'm gonna hit send and I get back as status to a hundred okay which is what we what we asked it to do and so if we go to our database and we go to data Explorer if we're lucky we're gonna see some information there and here it is in our online prod which is not what I expected to see so let me figure out what I did there did I have it I built it in debug right well I guess we won't worry too much about that but we have our items right here so you can see they are right there and let's see I've got an idea I've got a name I've got the description and then I've got a secondary ID and the ID is null so this doesn't necessarily look the way we want it to we also got capitalization in here which we don't necessarily want on a no sequel database we generally want camelcase in there so this doesn't look the way I wanted to so I'm gonna go ahead and delete it and take this out let's let's start over so what we need to do is go to our model and we need to annotate it so what we need to do is create a little bit of a bridge for working in c-sharp with properties that want capital letters and then working in no sequel which wants camelcase and we can easily do that in our model and this is where we will use Newton's soft or one of the areas we will use Newton's soft there we go all right so I'm gonna bracket there not a comma and Newton soft JSON JSON property and we are gonna set the property name equal to ID and what this is gonna do is it's going to allow us to have an ID object alright ID property with a capital I on our object and in the database it is gonna be mapped to a lowercase ID and so we'll take that and we'll just copy it and we will paste it over the rest of our fields and then we will change it to the name of the field that we want in the database and that's the same thing I said we just want lowercase that's what we're just trying to accomplish with this all right we're gonna stop that we're gonna clearly say hey I want debug here and then we're gonna hit go and I'm gonna go back to postman and I'm gonna do exactly the same thing okay it's ready for me now alright we're gonna send now I'm gonna get our okay 200 we're gonna go back here we're going to hit refresh all right now you can see you've got your name you got a description you get your ID and so there's only one ID where there was an underside II there was an uppercase ID before and the uppercase I did was was null so we didn't give it an ID it generates one on its own you can specify it you can pass it to it but if you leave it out it's gonna generate one on its own and it will be in that lower case lower case tense right there and so that looks good and now we can do a second one just to give us something to look at we will call it my item two my item two description will be wicked creative here and we'll hit refresh and then we can see we've got my item two my item two description right there and then that gives us something to check our get for from so I get request to HTTP logos five thousand one if you're using this postman for the first time with asp.net core you might get a warning here from postman about the SSL certificate being self signed if you do you can go to preferences or I think it's called settings here you want this SSL certificate verification off because it's just a self signed certificate the API is using HTTPS by default which is kind of nice but it is just a self signed certificate so postman might throw up a warning or block you unless you turn that off so you know if you're dealing with a situation where you need that verification you can turn it on but in this case it just gets in our way you need to turn it off and you should be able to pull up a single one using an ID so we'll grab this ID say hey I just want all of it I just want that one record and so you paste this afterwards and you just get the one and if you leave that off you should get both mom beautiful all right so let's just wrap this up with a put request and a delete request real quick and then we will call it a wrap so back to our controller we'll do a put request and we'd just like we did up here in fact I'm just gonna go ahead and copy that let's just get the basic syntax from up here try to speed things up a little bit that we don't want get we want put and here we don't want to get we want to put I probably would have been just as quick to type it and yeah actually we don't want the we won't want an iqueryable we want a task with an action result I should just type from scratch so it again when I try to be lazy I end up working harder all right I action results put requests and we do need a string and but we do also need from the body of the requests we are going to need the item so that we can update it and here we want to await as usual as usual we're going to use our document client and it's giving me a trouble there because I did not put a sink up front and it clearly didn't do something else either oops we're getting crazy here there we go document client replace document a sink and there we go URI factory is going to give us our reference and we're looking for a very specific reference here that will so we want to go right into the database we want to go into the collection and then we want to go right into the item using the ID and then we return it we return our okay okay lastly we just need to delete it we'll just type this from scratch after our last experience for some reason I didn't think that was right was it lighten up with the intellisense like I liked it's only how dependent we get on and tell us sense to tell us that we're doing things that we know we're doing right but we don't feel you're doing right till they light up to a certain color all right crazy with the tabs and we're waiting this this is everything's async and this is delete document async and we're just URI factoring we want to create a document URI and this one's gonna be other we're very specific as well because we want to make sure we're grabbing the actual document that we intend to so it's just like the put except we're not passing the body into it in fact uh I forgot to pass the body to it here and let's go back up to the foot I should have had a comma here and then return or actually passed in the item because otherwise it's got nothing to update off up so that's the difference between that didn't seem right that's the difference between the delete and the replace is not only that deer calling a different different item here but you're actually passing the object into it to be updated on to put requests whereas they don't you know care on the the delete you're just giving it the ID so okay and I actually seeing I have a missing bracket here in my ID that's I think actually stopping it from starting let's fix that alright once it's fired up we can create a put request so in postman put request same address and we want to actually capture the ID of one of our items so let's go to well we'll just go to this one here grab it I want to put that up here in the URL alright and we want to create our raw JSON so we want to set our headers to content tight application JSON that's not so just this one here application JSON and then their body we want our name we want the description and we want the ID there alright and then we can go ahead and send that actually let's change it alright send that and we can refresh our data here my item new name my item with a new description so there we go and you know what let's go ahead and just copy that ID again get on the clipboard but delete same address slash item slash ID number send get our 200 we go verify in our database we refresh now we see only one item left so we get rid of it so that's just a simple rest set up with cosmos DB on the backend and asp.net core doing the the rest operations and I think so for each controller you just create basically the same thing I mean very little needs to change here other than a type of item that you're passing in for a post or a put request and then up top you want to reset your your collections I haven't quite figured out what why it's reading the the production config string versus that so this that's something that I did incorrectly and I need to monkey with but you know if you're coming to this a little bit of knowledge you probably already know what I did wrong or you can figure it out on your own all right so when I go ahead and wrap it up there hopefully this is a some use to you this was kind of an interesting little project for for me got to stitch together some things that I haven't done before and it was it was fun to throw together so hopefully you found this this video useful and you enjoyed it and thanks for so much for watching take care
Info
Channel: Andy Julow
Views: 9,712
Rating: undefined out of 5
Keywords:
Id: hiaZInEc5bQ
Channel Id: undefined
Length: 53min 58sec (3238 seconds)
Published: Tue Aug 20 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.