Transcripts
1. Introduction: Hey guys, welcome to my brand new course, ASP.net core, solid and clean architecture. I'm your instructor, Travolta Williams. I have over 10 years experience as a software engineer and a lecturer. In this course, we're going to be looking at quite a few things. We're going to look at implementing solid principles and clean architecture in an ASP.net Core application. Along the way, we're going to work with advanced tools like mediator or bottom-up or Fluent API for validation, we'll be building Global Exception Handling and logging. I will be looking at the C QRS pattern, which is a wonderful pattern that keeps our code segregated and in bite-size is for maximum integration and extensibility. At the end of this course, we're going to understand how to do unit testing, how to integrate third-party services into an application and look at how we can deploy it for production purposes. Though, the requirements for this course include Visual Studio 2019 and dotnet five, or the latest version. At the time you are doing this course, everything we're going to be doing is future-proof and these principles can be transferred to the latest versions with no problem. To get the most out of this course, I do recommend that you have some amount of C Sharp and dotnet programming knowledge as well as database development knowledge. Either way, I will make the content very beginner friendly and you should have absolutely no problem following along and know that you have all the information you need. I'll see you soon in the course.
2. Understanding Clean Architecture: All right, so we've looked at the principles that govern clean application architecture. Now let us look at what exactly we mean by clean architecture. Because clean architecture doesn't necessarily mean good software. Good is very relative to who is viewing it. For instance, if you were tasked with developing a lead management system for the HR department and you did it, and HR is happy, then it's good software. However, if you didn't do it following these principles, then it could be seen as bad software, but practices bud software development in general, by your team or whoever your successor is when they're trying to maintain this application. So clean architecture is not necessarily directly proportional to good software. It depends on who is the recipient out and at what point. So let us just discuss everything that goes into software development and the different types of architecture so that we can fully appreciate when we would need to step up our design considerations a bit more and what the pros and cons of this are. So the first one that we'll want to look at would be all in one architecture. And I'll all in one architecture, it's easier to deliver. It can be stable and seen as a long-term solution. All in one architecture could easily be when we scuffled at brand new is be dotnet Core application. Or if this is your first time doing ASP.net Core, you're probably more familiar with Ruby on Rails or larva or jungle. Once you have that general project layout, that could easily be seen as an all-in-one architecture. You have all the folders and all you have to do is create your files, create your full does that separation of concerns right there and everything is good, the software will work. However, it is definitely difficult to enforce solid principles. The more that you have to pack into this all in one architecture is the more code smells and bad practices that might have to be compromised. All right, so as it grows, it becomes harder to maintain and its testability. And when we sit this stability, we mean like with debugging and by extension unit testing, the ability to test two units of this all in one architecture. That ability diminishes as the application roles. Next up, we want to look at layered architecture. And all layered architecture is a step above all in one where instead of separating our modules or our chunks off code and files, sorry, by folders, we start looking at creating projects and referencing these projects. So in ESP dotnet Core and in its obligations in general, you can develop class libraries where you put all of your classes and you just make reference to them as you need to. And then you'd end up with different layers. You have a layer that deals with the UI stuff. You have a layer that needs with the business logic. And you have a layer that taxes the inflammation between database and up business logic. And then you end up with a layered our application. Now, it's easier, definitely to enforce solid principles and it's easier to minton larger code bases. However, this still access one application because all of these layers are still somewhat dependent on each other. So we're still not fully taking advantage of the whole dependency inversion principle, as well as the whole loose coupling principle. And now finally, we're looking at The only on architecture which is largely taught it as clean architecture. Now when we talk about only on architecture, we're talking about different way of seeing layers. And at this stage, we want to support any kind of application. Want to have everything very modular so we can put in a new module without disrupting the rest of the code base. We can take auto module and we can make changes and test more easily without disrupting existing parts of the application. So when we talk about the Odeon architecture, we're talking about maybe having some client application for the user interface. This could be an ASP.net Web application. This could be a mobile application. We're talking about both having API services that will sit on top of the obligation on core. And these API services, once again, would be open source. It doesn't really care what the client is and risks is what we'll be using. But it doesn't care if it's a mobile client, it doesn't care if it's a web client, a blazer client, and anger a client, It's just taxiing data from the database to whoever is listening. And Buck, No, testing becomes much easier to do because we can test the different layers independently and then we can test how the layers interact with each other. So if anything is introduced to break any of these existing interaction, then we can detect it more easily. And from early null for the infrastructure we're talking about the module. So we're talking about logging data access. If we have more than one data bases that we have to be dealing with, we should be able to put in a brand new layer to talk to undo database without disrupting anything that the existing architecture was doing with the old database. We can put in logging, we can put in mapping, validations. Everything can go in and our commode without disrupting our application. So cons, however, there is a learning curve because you need to know what was weird and there are different interpretations of this kind of architecture. So you may see different flavors. If you read two or three different sources, you're going to see two or three different implementations of its enemy. Wonder which one is the one I should use? Of course, context determines a lot of what you and other developers do, the decisions you make in your architecture. And so, you know, you just have to use that, just get the knowledge and use your context as your guide and other con to this kind of architectures that it can be very time-consuming because there are a lot of their lot more files that will be involved when we could do something with two files now it will probably require five. However, once again, the benefits would be that we have loose carpeting and more testability. So with all of that said, be careful. Not every application needs, quote unquote, clean architecture. Good software meets the business needs and maintainable software increases the lifespan of the software. So you have to strike a balance at all times. Don't see that every project you have to do is going to need clean architecture, starts small and extend as needed.
3. What We Will Be Building: Welcome back. In this video, we'll be taking a look at the application that we want to build or rebuild. So on my screen I have a lead management system that was built with a speed on it core 3.1. And you see all of the things that were implemented inside of this solution. Now, it looks good and it does what it was designed to do. It delivered leave management capabilities to the HR department. You can find the source code for this application on my GitHub account and you can look for the repository leave dash management, dash dotnet core. In fact, this is the application that was built in my course, complete ASP.net Core and Entity Framework Development. Why did it was perfect for beginners? It's true with a lot of code smells and inefficient ways of writing the code which a beginner can pick up on. But then as you get more intermediate to advanced, you'll start realizing that they are better ways to do these things. And that's exactly what we'll be discussing in this course. Now, I'll just take and look at the project structure. And once again, it is completely optional to download it. You can just follow along, but I will be showing you some of the weak points of the architecture and some of the decisions that were made during the development of this application. One, you will see that this only has one project. Now if you remember earlier when we looked at the different kinds of project architectures, this would fall right into that all in one architecture wherever thing is just in one project, easy to access. So once again, for beginner, that's fine because you want views, you get to the views you want me to go to data. It's all their separation of concerns at this point is limited and restricted to just folders. However, in the bigger picture, and when you want to add more things, this project can get very fat as you try to extend it, so it will be good to break it out. Another thing our code smell that we would want to address is repetition of code. So even in building our data models, you'd see that we kind of repeat some of the properties. We have ID there, we have ID here again, those little things in some of the controllers, we repeat code when we don't necessarily need to. And then while we're on the topic of the controllers, there are what we'll call fat controllers here. So we're doing a lot of business logic, a lot of processing right here in the controller, which makes it very heavy. So you want to kind of obstruct those business logic processes away from your controllers. Now, outside of the development reasons for redesigning and re-architecting this application from a business side or business perspective, one, HR wants to extend this application the way it's built, right? No, it is fairly difficult to do that without upsetting existing code. That's 1, 2, it's not very testable in its current state. So even if we try extending it, we are going to have more owns off testing because we have to do more regression testing and manual testing on that. And then three, when we do it in a clean architecture wave, we actually have the option to extend it to different kind of client applications and potentially deployed as software as a service where the API is carrying out all of the business logic and interaction with the database and the client application is completely anonymous that we had. This client application could have been an MVC application, he could have been a blazer application, it could have been a mobile app. So that is one of the value proposition reasons for why key and architecture may be implemented. So going back to what we said earlier, good software is software that does its job, right? No, this is good software. The HR department is quiet, happy with it and how it does, what it needs to do. However, when they start asking for more things, we realize on our technical said that it becomes a bit more difficult to extend it, to modify it, to test it. So where we are protecting it so that we can still deliver all the functionality that the HR department wants while retaining the ability to maintain it in a very efficient manner. In our next lesson, we'll start setting up the solution for our new architecture.
4. Setting Up Solution: Welcome back. In our first practical lesson, we're going to be setting up our solution. So I have Visual Studio 2019 open. If you don't have Visual Studio 2019 already installed, you can easily go to Visual Studio dot Microsoft.com slash VS and download the community edition. The Community Edition is free for educational and individual usage. Of course, if you're, if you're a commercial than you're expected to get the professional or the enterprise version. Once you have downloaded it, you will be presented with an installer. And what you really need from this installer, at the very least is the ASP.net and web development workload. So by ticking it, you're indicating that you want that work through it. You'll see all the workloads available. You can take them if you wish. Of course, the more you take is the more you need to download. But for this course you really just need this one which will give you all of the tools needed for any ASP.net Web application as well as dotnet five. So once you've completed that setup or if you already had it installed and continue along with me. And we'll go to create a new projects. So what we're going to do is create a blank solution. I have in my recent project templates. But if you do on top of that, you can always search for blank solution in this area and then you'll get an empty solution containing no projects. So that's what we want and we're going to be calling this project H are dots leave management. Now there's no particular reason for me to call it this. It's a lead management application is being built for the HR department. If it was being built for a company, then you probably would want to say company dot the type of system. So if you want, you can go ahead and use my name or change that name accordingly. But once you have put in a name that is valid, then you can go ahead and click Create. Now we won't be writing any code in this lesson, but we will be putting out that project structure as we need it for the duration of this course or this whole project. So the first thing I'm going to do is add a folder and I'm going to call this one SRC, short for source, and another folder that we're going to call tests. Now under SRC, I'm going to add a few more folders. I'm going to call this one the API, because that's what will interact with our client application. I'm going to have another one called core, one more called infra structure, and finally, one for the UI. So that's all we're doing for this lesson. We're just setting up the project structure. So when we come back, we'll start creating the different projects that will go in each of these sections.
5. Creating The Domain Project: All right guys, welcome back. In this lesson, we'll be creating our domain projects. So our domain projects will go underneath our core folder inside of our application, we can go ahead and add a new project. And what we're going to be doing is adding it as a class library, C-sharp Docx library of type dotnet standard. All right, so it's not in a starboard allows code sharing across many different platforms. So we'll just use that one for all of our class libraries. And we're calling it HR dot leave management domain since it will be used to store all of our domain objects or entity classes that get translated into our database tables. So I can go ahead and hit Next. And the target framework here is dotnet standard 2.1. Go ahead and hit Create. Now once that is done, we get our default class one, which we can actually delete and then start populating with our classes. So going back to our project on GitHub, just quickly, you'd see that the domain classes were held inside of the project in a folder called data. So our domain classes included leave allocation, leave requests, and leave type. If you want, you can go ahead and retrieve them, but I will be going through them anyway. So if you just take a quick look at these classes, you'd see that each one had an ID, and we also had a bunch of properties in each one. So when properties that kind of repeated would include date created between the leaf type and the leaf allocation record. And then it's inconsistent because leave management or sorry, leave requests doesn't really have that. So we'll be fixing some of those inconsistencies, as well as reducing the repetitions between them. So I'm going to start off with leaf types. So I'm just going to go to the project click Add new class. We're calling it leaf type. And I'll make this public forum No, inside leaf type, what we're going to have our fields for ID, name, Default days, and they each created. Now I did specify that date field, like date created is really an audit field and this needs to be phone across the different classes. So I'm just going to go ahead and add all the entity classes so you can pause, replicate that. For the leave request, we're going to have an ID field, start dates and end dates, leaf type. The data requested, requests, comments, date, axon, and approved in the form of a nullable boolean, and canceled in the form of a Boolean. So you can go ahead and replicate that. No, if you're comparing this with what is on the GitHub reference, there are some fields missing, which we're not quite ready for just yet in the form of the employee, which would be some user related data. We don't have that yet, so we're not prioritizing that we can introduce that later on through some migrations. Know the third entity that we're going to add is leave allocation. And for this one we're going to have the ID, the number of days, the date created, leave type, leaf type ID, and period. Once again, I've kind of omitted the employee related fields as well. Those data annotations. Now in terms of repeating fields versus the need to have consistency with at least the visa need to be repeated, namely the date created. Usually when we're talking about auditing, we want to have date created, who created it, the modified date, and who last modified it, right? So that way we can keep track of everything happening across it as almost all fucked. We would actually solve some of the things with the whole referencing to the employee and so on with these auditing feels. So what I'm going to do is introduce another folder in this domain project. And I'm going to call this folder common. And then inside of common, I will have a class that I'm going to call this domain entity. No one knows my base domain entity have, well, it's good to have all of the base fields that I know every class or every domain entities going to need. I'm going to make this an abstract class so that it cannot be instantiated on its own. However, it can be inherited by everyone else. So this is going to be ID because every table is going to need an ID. I'm also going to have for other fields, one for the date created, then created by then a date-time for the Last-Modified data and another one for the last modified by. Now with this base domain entity, I can know let everybody else inherits. I can just go back to my actual entity class and let it inherit from the base domain entity, including any missing references using control dot. And there you see that these fields that I have INSEAD leave allocation that have the same names as the ones in based on an entity are lighting up. So that means I don't need to repeat them. Alright. That looks a little cleaner. All right, So I go and leave requests than I do the same. Go ahead and inherit include the missing references and then all of a sudden I don't need to see ID over here either. And then in leaf type, I do just the same thing, including missing references. And then I can remove all the repeated fields. So are based on an entity gives us access to certain fields that are expected to repeat across every entity as we all want them to have these. All right. So with all of those changes made, let's go ahead and hit Control Shift and B to do a build and we have a successful build. So we can move on to the next task.
6. Creating the Application Project: Hey guys, welcome back. In this lesson we'll be discussing our application core layer. Now the purpose of this layer is to sit in between our application, application being whatever would be wanting to access the database and the database. So in this layer will be defining all of those access parameters to mediate between the calling application and the database itself. So this project will sit directly inside of our core folder. So let's go ahead and add a new class library. And I wanted to be calling this one HR dots, leave management dot application. Go ahead and add it once again, we're using dotnet just under 2.1 and go ahead and create. So now that we have our class library, I'm going to remove the default class, and then I'm going to add two new folders to this project. So the first one is going to be persistence and then encipher systems, we're going to have another folder called contracts. So inside of contracts we are going to be defining all of the obstructions for our repositories. Now the jury's out on what are the best ways to implement this. Once again, there are a few opinions is the proposed architecture, and there are a few opinions and some opinions get implemented based on context. But I'm just going to explain why we're using the repository pattern. So one, in the, in keeping with the dry principles, we don't want to repeat queries too often. Also, you might have specialized operations that you need to carry out on any one of these domain objects. So when you define a repository per domain object, then you can have custom quote or customized methods inside of that repository for these, right? No, we're not implementing the repository though, we're just defining the contract. So the contract here, the first one will be an interface and I'm going to call it ij generic repository. I said class here, making a class. But even if you use caution that you can just make it a public interface. Now we're going to tell this interface that it will be implemented relative to a class called T. So we're just using generics in keeping with the name generic repository so that any one of our domain objects can be used to access database related functionality through this interface. So some of the methods that we're going to implement in this interface include a getMethod, so we have task T. And then you can just go ahead and include any missing libraries. We're also going to have task, get all what this one is returning. I read only lists, know if it's the first time you're seeing this data type. Usually, and personally I usually use a list or a nihilist or one of those more popular Collection datatypes I read on a list. That's benefit is that it keeps it in a read-only state so it cannot get modified once it has been pulled from the data mesons sent over. So less trucking and less database related operations after the data pool. So we'll look at it in a few, but that's the datatype I'm using. Once again, you could easily have used any other collection type of type T. Could have been a list, could have been a collection. But then I read on the list is serving the purpose I wanted to. We're going to have the task to add on where also Linda have tasks to update and delete. So you see that the generic repositories not only generic for t, but it's also a generic in terms of the functionality provides which are the basic and standard crud functions. There's also a naming convention where people would put on the word async because you can see where all using tasks here. So some times you would see then define it as a sink. So it's obvious that this is going to be an async function. So you couldn't follow that name convention if you want. I am not going to, so we can move forward. Now the thing with the generic repository is that once again, it's just the generic crud functions that every single database table may ever need to perform. However, when you have something specific to leave allocation operation or a leave request operation, you can't build the salt and the code might get missy if you try to build it out in certain parts of the application. So what we're going to do is in comment specific repositories based on our generic repository or to extend our generic functionality, but specific to either our anyone of our domain objects. So I'm just going to go back to contracts and I'm going to add a new item in the form of a class once again, but it's really going to be an interface and I'm going to call it, I leave request repository. Once that's created, we make it into a public interface. And then I let it know that it is extending generic repository and it is specific to our class type of leave request. Alright, so leave request is our domain. Remember that this was relative to type t. So this is the T that we're passing in, but then you'll see here that it doesn't know anything about say 70. So we need to add reference to HR management domain. So you can see that in all honesty, that hasn't quite worked for me when I use their side, just go over to dependencies. Let me do that again, sorry, dependencies at project or a friend's. And then I just take the reference to the domain objects. Click Okay. And then I can know, include that using statement here. All right, so inside of this repository, then if we have specialized functions relative to only leave requests, then we can define them here with the modelling of the generic repository and without the other specific repositories needing to see it, we want to keep everything kind of contained in one place. So following this example, I'm just going to go ahead and defend the other repositories. I leave allocation and I'll leave type repositories. So once that activity is done, we end up with two other interfaces. So we have our generic repository. We have or I leave allocation repository, which is implementing or inheriting or other I generic repository relative to leave allocation. And then we have that again for leaf type with I leave type repository extending generic relative to leave type. So that's really it for this lesson where they're sitting up the contracts. And those contracts are in the forums of these interfaces. Because remember that we don't want to directly interrupt with the code when we're calling what we're going to be dealing with abstractions that way we can make code changes without disrupting too much off the quote from the client-side application.
7. Implementing Automapper: Hey guys, welcome back. In this lesson we are going to be sitting up auto mapper. Know if you don't know what autumn upper is, that's no problem. It is really just a library that helps us to convert between one data type to another. Simpler, right? So the context behind why it will be useful is that we'll be implementing the mediator pattern in a following video. But that mediates or pattern will be acting as a messaging mechanism between what is coming from the plant and what needs to go to the database. It is, generally speaking, bad practice to interact directly with the domain objects, at least before we get to the repository. That means we shouldn't be allowing any calling application and the client, any application that is talking to our application on core to be sending us directly or direct objects off the domain object type. Instead, we create obstruction. So we create data transfer objects or view models. You can call them either one, but they're really just crosses that kind of look like the domain object, but have restrictions based on the operation. So edit operation has different requirements from maybe a create operation. And so you would present the id 1 and id wouldn't be presented in the other. Once again, all you have are obstructions for that. You can get granular or you can keep it general. That's up to you. The point is alter mapper will help with the conversion between the obstructed or the client friendly version of this object and the database friendly version of this object. This is going to promote loose coupling throughout our application. So with all that said, the setup is not going to be very complicated for this one, what I'm going to do inside of this application project is create a new folder and we're just going to call it profiles though. Profile is basically just a configuration file for automobile tunnel that it is allowed to convert between one data type to another. So I'm going to create a new dress inside of this folder. And I'm going to call it mapping profile. And then I need to introduce and I'm going to make this public of course, but I need to go to NuGet and get our altar Mapper Library. So I'm just going to right-click money. Can you get library? And then I'm going to search for ultimate author. And the one I'm interested in is ultimate Bardot extensions for dependency injection. We'll come with all the dependencies including the base ultimate PR library and anything else needed so it can be used for dependency injection accordingly. So I'm just going to go ahead and install that one. Of course, accepting and allowing any problems that come up on once that is installed. Instead of mapping profile, I'm going to let it inherit from a class called profile. And this class is phoned in auto Mapper Library. Alright, so we have the profile setup. That's good. Well the next thing that I'm going to define would be my data transfer objects. So I've created a new folder inside of this application project. And in this folder I have a few files. One, I have a common file or folder other with a common file called base detail. So similar to haul and we're sitting on board domain objects, we have a common and then where the base domain entity, which had some base properties that should be across all of these domain objects. It's the same way that I've just made based detail. And the common property would be ID. I don't need the date created and so on with the DTLs, that's just for auditing, so that's in the background. So what I really need shared across them all would be the ID. And then inside of each of these, which I haven't done those yet, I will then have other properties that I know each of them need to have in order to successfully taxi useful information for me. So this part is as easy as going to leave type and taking the properties that you know, you would want to allow your application or your users to be able to interrupt it. So that would leave requests. These are all required properties. I'm just literally, as you see, I'm just copying and pasting the hard part really was just defining the base detail and letting the inheritance put on that IDs and not details, talk to details. So you see here that leaf type is being retained. You see leaf type is being retained. You don't let the DTO know about the domain. All right, so this should be leaf type DTO, and this should also believed that DTO, and as a rule of thumb, details speeds and DTLs. Autumn upper know is what will allow the conversion between one or the DTO and the domain. So instead of mapping profile, we're going to have a constructor. So you can just save CTO tub, tub that generates our constructor. And then I'm going to have a line that says Create map. And create might be saying between a source and a destination. So T source would be, let's just say leave request would be my source. That's the domain object. And Leave request, DTO, and that would be my data transfer object. All right, so we're creating a mapping configuration between leave request and the request DTO and just close with the parentheses. And then I can extend this again and tell it that it can reverse MOP. And ultimate PR is variable for I have seen applications where people put some amount of business logic in autumn upper itself when working with complex and probably cross-domain applications, you probably want a domain or a detail to be able to convert to a domain objects on one set and a domain objects on another. So you could actually have multiple mapping configurations from one object to another. You could also create custom converters to see this member goes directly into that matching member on the other side, there are a number of things you can do with that. That could be a whole course by itself. But today we'll just keep it simple with our simple configuration requirements. So we have leave a location and then we have the matching DTO. And then finally we have leaf type. Now I want to point something out to actually alluded to it earlier, but I want to mention it again. There are times when you want to get a bit more granular or have details specific to a certain purpose. So let's look at leave requests. Leave requests detail has quite a few fields. The purpose of a DTO is to limit the number of fields. So reduce over posting and are under posting or providing too much information to the user. So what I'm trying to see is that in the event that you want to see a leave request, then all of this data becomes relevant. But then in the event that you really just need to list it or show a listing of all the leave requests. You probably don't need all this data that the user doesn't need to see all of this data when they're requesting just the list. They definitely won't need to see the comments at that point. They probably don't need to see the date action approved or canceled when they are listing. So in that case, what tends to happen is that you probably want to break it out a bit more. So sometimes you would end up with a folder inside of the details. And that folder would be specific to maybe leave request. And then inside leave requests, you have all of the details that differ. Right. So yeah, leave request detail. That's fine. This one would be for the details what well, maybe I have another details specific to listing. So you'd have requests list D2, which yes, it would have the BSD TO also. But then it has far fewer properties in it. So you probably just need leaf type the date it was requested and if it hasn't been approved or not. All right. So once again, this is all relative to whatever you think you need to provide. But I'm just seeing that in this situation, you probably don't want to put every single thing in one detail and then always use this detail every time a request is made, no matter how miniscule their request is or how little data there really need in that situation. So that is the purpose of the DTO. So actually with those changes, I don't know, I broke some code. Let's firstly, let me update the namespace on this leave requests detail. So that's it knows it's new location and then I have to update my mapping to know know that it has leave requests, list, DTO, tore a boat also. So just go ahead and update those. And then let's do a build or I build a successful. Last thing that we're going to do here is setup on AI service collection registration method. So the purpose of this is, well, in keeping with dependency injection, we want to be able to have all of our components, all of our injectable components defined instead of the application level. But then any client application can go ahead and just call the method and have it registered inside itself. When we get there, you'll see it, but I'll just do this right? And also this would be applications, services, registration. So this is going to be a public static class and it's going to have a method that I'm going to call public static void Configure Application Services, which takes a parameter of AI service collection. So we can just go ahead and include any missing references. And then inside of this we get to see services dot add, auto, mapper. Or I'd say if you're familiar with dotnet Core and dependency injection, you'd see that this is just a service called action that you would have seen in the starter file for any dotnet Core application. So the thing is that we're abstracting it to just this project so that when we have that dotnet Core application, we can just call this method. And then it would go ahead and register every service that is defined inside of that method. So we are adding auto mapper and then I'm going to say assembly dot, get executing assembly. Go ahead and add any missing references. Now if you're familiar with automatic or you worked with it before, but you're not necessarily familiar with this. You'd probably be more familiar with seeing something like type of an then would find the mapping profile and then that would allow us to register this profile has the autumn upper configuration in general. The thing with this is that for every mapping profile you have, you probably have to repeat this line. And you, based on the size of your application, you may end up with multiple mapping profiles because I've seen where you can have mapping profiles per domain object and pr, pr detail domain object peering based on the number of databases are domains that are present, right? So you are even at an application level, one application or one client application may have different Mapping requirements from another so you keep them separate. So my point is that by saying assembly dot get executing assembly, it will just traverse for every mapping profile that has that inheritance pretty much and it will just work. All right, no easier way to see it. So that is how we're registering our autumn upper in our dependency injection. And that is setting up autumn mapper in general. So if we build and it is successful, then we come back next and continue our application setup.
8. Create Queries with MediatR: Welcome back guys. In this lesson we'll be implementing two patterns that help us to promote loose coupling through our application. They are the mediator pattern and the C QRS pattern. Know the mediator pattern is seen as a behavioral pattern because it allows you to define exactly how a set of objects interact with each other. In more simple terms, you make a request and you get a result based on your request. Every time you make this request, you can expect this kind of result that is a kind of consistency that it helps you to implement. It also helps you to obstruct all of that logic associated with that request from the calling application. Now to see QRS pattern and see QRS is short for command and Query Responsibility Segregation. It helps you to separate the read and write operations from any data store. So in the name command and query command is anything that is going to augment the data, any write operation, any update operation. And a query is me reading the data. So you can always know that when you're doing something that's going to augment the data, it's a command. If you read in data it's a query. So let us get started. We're going to go over to new get firstly, and we're going to grab the mediator package, which is interestingly enough, authored by the same person who gave us all the mapper. And that is Jimmy BullGuard. So we're going to go ahead and install that. And once that is done, we're going to come back to our Configure Application Services method and we're going to make a few changes here. So some of those were oversights on my part initially, and I apologize. So we're changing this from a voice to an AI service called electron, that's one. And then we're adding these two lines. We're seeing services dot add mediator, assembly, dot gif executing assembly, just let the auto mapper and we're returning services. So for the ADD immediate term, just going to go ahead and include the missing references. And there we go. So that is what this service registration or AI service collection method should look like. Now that we've done that part, let us just backtrack and we gather our thoughts and thinkable what we need to do, we need to implement the PQRS patterns. So the CQ are spots are, and once again separates commands from quarries. In other words, we need folder for commands, we need a folder for our queries. Either Command or query will be handled by what we call a handler. So when you make a request, it gets handled and the handler is going to be either a command to augment the data or something to return data. So you see that there are quite a few moving parts at this point. And this is why this can have so many opinions on how it gets implemented. So once again, I'm not prescribing an implementation method or folder structure, but I'm just helping you to visualize it. You may change it based on your needs. But this is whole world went to approach it so much it creates a new folder inside of or obligation project, I'm calling it features. And then inside of the features, we're going to have folders per domain type. And the reason I say domain type is that the feature of the application is relative to what you're going to be doing against our domain object against the deeds of these, right? So I'm going to be doing it in chunks based on the table or tables specific features that the application offers. So let's start off with a simple one like leave types. Inside of features we're going to have leave type. I'm going to just realize this leaf types. And then inside of the leaf types folder, I'm going to have a few other folders. I'm going to have request handlers, then inside handlers on when to have commands and queries. And that is the folder structure that I'll be working with. Now, like I said, opinions differ on hold. A polar structure could look. Some implementations would actually have the top level for the dummy in the feature name. And then they would have commands and queries. And then inside of commands or queries, there is where they would have all of the requests or have a folder, sorry, per query, let's say inside query, they would have a folder for the specific query, say get leaf type list, that will be the query. So inside of that you have the good leaf type request, get leaf type RequestHandler and the BTO specific to it. So once again, there are many ways that you may see this implemented, but the concepts remain the same. You just want to make sure that you can identify commands differently from your queries and you know where your requests are. Requests are relative to a 100. So now that we have the folder structure in place, let us get started with some coding. So inside the requests, the first request that I want is to get the leaf type list. So naming convention wise, you always want to imply what the request is for or what kind of requests in the nein, right? So get leave type list and I'm going to specify requests. Alright. So get leaf type list request. And this is a public class and it is going to inherit from IO requests. So this is courtesy of mediator, and it is going to then ask, what should this request expect in return? So when I request using this datatype, what should I be getting back? Well, you shouldn't be getting back a list leaf type DTO, because once again, data transfer objects R1 will come in and what would never the domain objects. So that is our requests, though. There might be times when you may have parameters they can put in requests and so on. But this is a very simple one. I'm not going to get into any of those complications just yet. So that's our first request. Next up, we need something to handle this kind of requests. Now this is a GET request which makes it query. So inside of the handler section, I'm going to go to the queries folder and I'm going to say add a new class and I'm going to call this one the request handlers. So that is my naming convention. So I have a request by a name and then I just append handler. So we'll go ahead and add that one as a public class, which is going to inherit request handler once again, courtesy of mediator. And then I request handler says, Okay, what requests them ideally width, so its request is the GET requests. Alright, so that's the naming convention that I like to use. And more than that, do you see that naming convention a lot because then you can specifically tie our request to a handler by that similarly, one is the request, one is the request handler. So you can go ahead and include any missing references. So it says what requests that my handling and what should I be expecting to return. So the return type of the request should be the return type. You been here. So it is expected to return leave type DTO. What I listed off leaf diabetes. All right, so now that that's done, we still have our red line. This red line is because we need to implement the interface. So almost automatically, we actually get a method here that says handled. It gets the request as the parameter. And then here is where we write all of the code to handle whenever this request comes in. Now before we start handling any request here we have a few dependencies. One, we need to be able to talk to the database. Now we haven't implemented anything, but we do have our persistence contracts. So these are contracts that will actually talk to the database. So we're still not directly interacting with the domain here at all. All right, so I need to inject the missing references. So using S3 or tub, tub, I'm going to get constructor and then I'm going to inject and I leave type repository. So I'm just going to go ahead and add any missing references. And then having typed it in the constructor, I can know, say create and assign a field. All right, there we go. And I tend to use the underscore because I like to see the underscore. So I know that this is definitely the private field in the class, so that is my naming convention. You don't necessarily have cause you see the auto-generated code didn't give you an underscore, so that's fine. Now another thing that I would want to inject is I mapper, because I do need to do some mapping when I get the data from the database, when this repository returns the domain objects, I need to convert them to details to send the buck. So I need automaker also, so I am upper, include missing references also go ahead and injected or creates NSA in the field. And there we go. So we have everything injected and ready. Now to implement this handler code, what I'm going to do is run a query against the leaf type repository. So I'm going to say var leaf fetch is equal to 08, leave therapy repository dot get all. This is a waiting. So that means this method needs to be asynchronous. And then you'll see that the one error goes away at least. And then what we need to return is the list of leaf type ETL. So I'm going to say return mapper dot map. And then this is where this comes in handy into leaf type detail, but it's a list of leaf type D2 or other. And what I will be mapping into that will be the list of domain objects coming from the query. And there you go. Everybody's happy. So ever request saying I want the leaf type lists, then we have the handler saying, Okay, I can handle this request for you. So that is what we mean by defining application behavior or defining how objects relate to each other. This request will always relate to this hunter. I believe if you try it out, multiple handlers for the same requests, you would actually run into runtime errors because of some ambiguity. So that is how we get to clearly define that when you made this request, you can always expect the behavior. Now let's look at another request and it's still going to be a query. But this time I want a spear. Leaf type. So I was saying that you may see requests or you may end up having fields or properties inside of her requests that will be needed in order to handle the specific operation. So what if we wanted to get elif type, not the whole list, but leaf type detail. So I'm going to create another question. And this one is going to be good. Leave type detail requests. Once again, remember to make it public. Now in this situation, I'm going to add up property and I'm going to say id. So we're getting the detail which means we want a specific record. And the best way to specify a record is by sending over the ID. So we need a handler for this request. So I'm going to add that handler and the other class or gets the type detail requests 100. There we go, make it public. No, one step I missed inside of the requests I needed to inherit from I requests. And it is only expecting a leaf type DTO one this time, right? So many requests DTO, go ahead and meet at all missing references. There we go. So the handler is now going to inherit from I request handler and go ahead and add missing references. It is going to be handling requests for good Leave type detail requests, and it is expected to return type ETL. So once all of those missing references have gone anything, go ahead and implement the interface, and then we start off with that. Now we're going to need basically the same injections that we had to use for. I'm just going to expedite that and copy and paste that code and just change the name of the constructor and go ahead and add all missing references. And once you have done all of that, you can get started on the specific code. So in this situation I would say var leave type is equal to and we are with the method call. So leave repository dot get and gets his expecting an ID. So I have the request object here inside of the portal handled methods. I can now say request dot i e. All right, again, where are we teaching? So this spacing, but I'm just showing you that that is how you would go about using those fields in your query, in your generic or near repository, sorry. So then when you have specific methods defined inside of leaf type repository, then you can have more specific fields for your requests that are needed to handle those kinds of operations. So once we get that, the next thing we need to do is return the leaf type DTO objects. So I'll say mapper, mapped into leaf type detail and we're mapping the type. So once again, only details. Now one thing to note is that in handlers who have a clear separation between the commands and the queries. But in requests there is no clear separation. So when requests start getting created for the commands, we have to rely on our eyes. We have two OK. We're not tired so that we don't get confused as to which request is for water. And I don't think that that's, it's very efficient. So I'm going to have commands inside of my requests, and I'm also going to have queries. So I know that whenever I want any requests, sorrow, and inquiries, I know exactly where to go. And the same for commands. So like I said, they are differing folder structure. Some people once again would have just said leave types. And then they would have had the commands. And then instead of the commands that would have the different commands with all of the resources needed for that specific command, the detail, the handler, the request, all in that sub folder. So as you break this odorless that you're going to need more files, more folders because you want to see a clear separation as much as possible. Once again, told this is implemented is relative to the creator. So I'm just going to move my queries that I've created so far into that specific folder. And of course, after update that namespace. And once I do that, I'm going to go ahead and update my handlers to know the new location of their corresponding requests. So with all of that, dawn, I'm going to do a build and build was successful and you see how it's all coming together. So if you want, you can go ahead and implement the other features we started with leaf types. You can at least implement the different, the two read or the two query operations relative to leave allocation and relative to leave request.
9. Finishing up Queries for MediatR: Welcome back guys who are continuing on the same vein of setting up our queries for our additional features. So we already went through doing it for leaf types. And the assignment was to do it for leave requests and leave allocations. So I've already done that and I'll just be walking you through. So if you haven't completed it, I'll go slowly enough and explain everything that's I'm doing so that you can replicate it. And if not, then we can just compare notes and feel free to let me know if you have done anything differently from how I have done it. So the first one, Let's start with leave allocations. So I have the queries and I have the, what I have the handlers and the requests, and I have the queries in either one. Now following the standard so far you'd see that or quests look fairly similar to what they looked like for a leaf types. We have the get leave allocation detailed request, which takes the ID. And I also have one for the list. Alright, so in the query is, I have a slight surprise for you. I don't know if you did it this way, but I'll walk you through exactly what has been done differently. So of course we're including the correct hello, The leave allocation repository rather as opposed to the leaf type repository because we're dealing with Levi locations right. Now in terms of our handling, you'd see that I have a method here that we did not go through in our, in our repository setup. I have get's leave allocation with details. Know the purpose of me specifying this is that when we get the details of the leaf allocation and if you need a reminder or leave allocation actually has a navigation property of leaf type, which means if I wanted to show What leave has, what number of b's after, show the name of that leaf icon, show the ID that's useless, right? Or the client or other control the id, they would need some amount of details of the leaf type on their side. So when I say with details, the purpose of that method is to do that include include that navigation property and everything so that by the time I get but that leave allocation, I would have all of that logic don't formula are ready and all I have to do is map and return. So at this point I'm just going to pause and 0 that you would see design pattern sometimes where they actually do all of that complex logic right here in the handler. So I've seen where they would actually directly connect to the context here in the handler. And then do all of those raw queries right here in the handler and then massages and then finally return what they need to return. So that's one design pattern you might see. And the other one is where we abstract away all of those logic and complex queries and business logic. And then mythos for the repository. No, I'm not saying one is better than the other because I had the approach has its pros or cons in terms of making methods that specifically deal with a scenario like getting the leaf allocation with the details. In terms of that, it could be good to put that in our repository, a method where you have a direct reference to where that logic art back kind of operation is happening because you may need to reuse that same kind of logic in multiple handlers. So that would actually reduce the repetition of the same kinds of queries and same kinds of inclusions and everything across multiple handlers. If you just have it in one repository space. The downside to repositories, however, is that because of how specific things can get? Yes, we have the generic stuff, but obviously this is not a generic command that every single feature our domain object me, use the same way. We have to have a specific method and then we're going to probably end up down a rabbit hole of having many specific methods in our repository. So that's one of the don't sites, the repository pattern know, like I said, if you do everything here in the handler, it would work. But once again, the downsides of that is you might need to repeat that kind of operation across multiple handlers. And then you end up repeating code in multiple places. And then modification might become difficult in the long run. So that's the surprise Alpha. So if you take a look at our interface, you'd see here that's actually have both for the list and for the individual. Now the jury's out on whether you need both. The reason I included the 14 and the list, of course, is that in the list, the leave allocations, I want to show the leave name and the d, The leave name and the dead leaf type name and the dy. So I would need to include the details here. And then if you're viewing one of them, you would want to see the details also. So I've just done that and I did the same thing. And when you're working with a lot of us, you need to navigate quickly. So I'm just going to use this blue arrow up top here to synchronize store this document is quickly and then leave requests. You see that I have similar methods because in the leave requests we need to know what kind of leave is being requested. And if I'm viewing the list, you'd want to see this leaf type was requested on this date, et cetera. Alright. Now, in terms of our leave requests, I have another slide surprise and I hope that you guys caught on and did this already. But the queries look fairly the same. But in terms the requests rather, but in terms of the return type, you'd notice that I have get leave request detail request showing the leave requests DTO, however, for the list and we discussed why we would separate the details earlier. We have leave requests list detail. So the list is determining a list of the details specialized for the list. And then the details is returning the detail with much more detail in it. For the queries. Similar concerns for the leave requests list, request handler. Of course it is returning the list of leave requests DTO. And the handler looks fairly similar words calling a delete request repository and calling the get leave requests with details similarity. When we're looking at the detail handler, it's the same thing except we're returning just the leaf requests detail, the lab setting this up, you might have noticed that you are getting a lot of red lines when you're types mismatched. And if you had the wrong data type here or the wrong would request type being referenced, everything would break. So it's very, very strict. If you told me that the request is expecting leave type request DTO, you cannot put any other datatype here in that handler that you're telling should deal with that request. Alright, so you can just be very, very careful about that. And once you get used to the pattern, these things will come more naturally. I can only assign if it's a bit frustrating initially. But once again, you have the code to reference so you can always just pause when you need to and replicate these bits of code in your application accordingly. Now in our next lesson, we're going to look at setting up our first command. And the command once again would augment the data in the database. So we're going to look at what it takes to setup, create command for any one of our domain objects.
10. Create Commands with MediatR: Welcome back guys. In this lesson we're going to be taking a look at setting up commands. So commands once again augment the data and we're going to be starting off with or create command. Now if you're looking at my Solution Explorer, you would notice that I have a few more files and I have a restructured D2L folder. So let me just collapse everything else so you can focus on that section. So in the early stages of planning out this kind of architecture, you're always going to be changing things around because it's good to get it right from null as opposed to later on when you have many different files and any more, for instance, the updates when you move, these other files are owned. So in the early stages of setting up the details, I had indicated that you probably would want to have a folder for the details and then have the different types of details in there. So we did that with the requests where we had the leave requests D2 and delete requests list detail instead leave request folder. So I've just extended that concept to the other details where I have a leave allocation folder and elif type folder and name this one leaf that deep detail. Let me just correct that. So inside of these folders, we're going to have the different details. And once again, it is not absolutely necessary for it to have multiple details or details specific to a purpose. But I'm going to show you why it is beneficial to do that sometimes. So with leaf type, the risk is low. We have leaf detail. All it requires is a name and the default number of days. That to me, that's simply enough. This data runs very little risk of over our underexposing anything for any of the operations we can create easily using this detail, we can list easily and we can look at the details. It's very simple. However, with leave requests, we had discussed that leave requests DTO has far more details in it. In a list setting, we don't need that many details. You really just need the details of the leaf type requested, the date it was requested, and if it's approved. Now in the Create, we once again don't need to ask for the consumer to provide all of those details. Because if you compare create tooth leave requests, we really don't need the date option. And when somebody is creating a leave request, when I am asking for a leave, there is no date actions. So that doesn't need to be provided. The date requested that can be put in by us, by the system. We don't need to get that from the user or from the consumer. The start and end date short. Those are essential. We need to know the leaf type. We don't need to know the entire object of the leaf type. We only need the identifier of the one that is being requested. And at that point, it's neither approved nor canceled. So I can have a specific detail and as I'm talking, I'm seeing that I have extra fusing this, but I have a create leave requests DTO, which is going to have the start date, the end date, the identifier for the leaf die being requested. And once again, the system can see the date requested. And I need to make this actually a nullable. So I'm going to use this opportunity to adjust this. So this needs to be nullable, but I don't need the sender in the Creates. I take it all together, create in the leave request, I'm going to make it nullable because deduction rule really mean when was it approved or canceled. Right? And then similarly, I'm just going to do this right down to the domain. Once again, it's good to see these things early and adjust them because we made the database as yet, so we don't have to worry about this ripping up too much. All right? So that is why I have a specific DTO for creating the leave request, different from listing, different from the details. In another, when we have the leave allocation and deallocation has a number of these, the details of the leaf type that is related to it, identifier and the period. It's similar to the leave request. I don't need the details of the leaf type at the type of time of creation. So when we talk about over posting, it means that we are giving the client application the opportunity to provide too much data. And then this is where hacking and bad data and people introducing anomalies in your database, that's how that happens. So this is a good way to restrict what can happen on an operation. So know that. Have a good understanding of why we have more details introduced. Let us take a look at the commands. So I'm going to start off with the easiest one, which is the leaf types commands. I'm going to go to request and I'm going to create a new class. And this Gaumont is going to be very specific create leaf type request. So that is the request to create a leaf type and went to go ahead and add it and make it a public class. And then this one is inheriting like we know from I request. And it will return, I'm going to make it return an integer 0. This integer will be. The name or sorry, the ID of the value or the record has been created. Alright, so you create, we're just going to tell you the ID of what you create it. We don't know what the client may need to do after creation, but we're giving them but the ID saved, they need to go to the details page afterwards. That's up to them, but we're telling you it was successful. Here's the ID for the new record. Now in the command, what we're going to have, as we know my null is our matching handler for this request. So we inherit from I request handler. And I request handler is going to be implementing the create leaf type request. Go ahead and include anything that is missing. And it will see it's returning an integer. Now when we implement the interface, you'll see that we get that task returning an integer and our handler. Now let's jump back over to their requests for a bit. And there are few patterns that you might see in the request for creation. One pattern is that people would actually write all the fields that when you are about to create a record for this year, sending over the requests to create record for that type. They would put in the fields in the actual request to see these are the fees that you're allowed to send with the request. And then in that moment, that request basically serves the purpose of my detail that I just defined here. No, I personally, I don't like to mix and match. I don't like to have details here, but then when it's I create, I have the fields here. And then, you know, I don't know where to go and I need to change what goes sometimes I don't remember. Instead, I keep everything at a detail level and the request is really just a mechanism to transfer the data. So the DTO represents the data and the request is just that transportation. With all that said, I'm going to introduce our property in this request for leave type DTO. So this property is what the consumer will fill with the request and send it over. So in our command we know that we say request dot leaf diabetes, that is where the data is. So of course, if we're going to be interacting with leaf types and auto mapper, then we will need our usual suspects in our constructor to be injected. And for speed again, those go back to one of the existing handlers and just copy and paste, change the constructor name. And then we just include any missing references. All right, to work smarter, not harder. So now that we have all the tools we need, how do we set up our handler to create this record? But before I move on, I'm going to make a slight adjustment. So one other pattern that you'll see, and I think I'm going to use it this time just because it's for me, it's cleaner instead of requests. So when we're dealing with commands, no, it's not going to be our request because I request is when you're asking for something, that command is when you're telling something. You want a command. So instead of us using request in the name here where we say create leaf type command. All right, So by renaming the source, I can load the IntelliSense to rename it across the board, but I will rename the file manually. So we have create leaf type commands, so we know for sure that this one is a command and it's obviously the handler is supposed to be carrying all its command. So Command Enter, alright, and update any other references are on the place. So I think that is actually cleaner and easier on the eyes. It's a distinguished between what I requests classes on what our command classes solve for our operation. What we're going to do is define the leaf type to be equal to. And then we're just going to use auto mapper to map into leave types. So we're mapping from the detail to the domain object this time our own. So mapper Dartmouth leaf type, interleaved tap our looking at request dot leaf type BTO. Alright, so you just go ahead and include anything that's missing. There we go. So now we say leave type is equal to get me the mopped version of that into the domain object. Then we carry out our, this is going to say leave type is equal to and our weights on we're going to call our leaf type repository dot add method blocks, right? So remember that it is actually returning the object or something off the same data type, right? So leave type is now going to be updated because after Entity Framework, which is what we're going to be using as our RM. After it does its operation is going to update the ID. So we're returning the object with updated ID and know that we have that ID. We can say return, leave type, dot ID. There we go. So know that command has been handled. Nolan, we look at it, we see that it was really not that complicated to do. It's just three lines where getting the request with the data. And then we're going to map it into the domain objects. We're going to then bosses or within a repository or for the operation to occur. And then we're just returning the idea and that is it for the create leaf type command. So you can actually just pause here and asked him to do it with the others. I'm going to do it and then we can compare notes. Alright, so I've skipped ahead and I've gone ahead and implemented the commands that create commands for the other two domain objects. So if you look at it carefully, you see that it's pretty much the same quote apart from the names and the repositories involved, it's pretty much the same code. So this is the leave allocation command handler. You can pause, you can replicate it if you need to. But that is our creates handler and our career quest. Just taking that create leave allegation detail instead of the leave allegation detail. All right. Similarly for the leave request, same code, same structure, right? And then the command, it's just taking the leave requests detail to create the request detail. So you see that all of these kind of look very similar. So you can go ahead and replicate those in your code. And when we come back, we will look at the other commands which would include update and delete.
11. Finishing up Commands with MediatR: All right guys, welcome back. As you noticed, my screen is blank. In this lesson, we're going to be talking about adding the update and delete handlers and all of the assets that go with them. So my screen is blank because I've added quite a few assets and I'm going to go through them one by one with you and you can go ahead and replicate them. But I want us to understand the decisions that we're making at this point as they are critical decisions. And this kind of architecture, the things you put in there, all decision based. Once again, they are relative to what you're working on, your team and the overall application on what you hope to accomplish. So let's start off by looking at the details. Where are they? Went through the details and we kind of separated them by folders so that we can see all the details relative to a domain type quite easily. But then at this point, we had one VT0 for leaf type, and we had said that, okay, that was low-risk because it could be used for many different operations. I have since broken it out into two. And the reason for this is you have created leaf tab2 different from leaf type D2 and all. Why? Because leaf type detail inherits from the base detail and base D2, remember gives us the ID, know the risk of this. And once again, purpose of the detail is to kind of reduce what we'll call over posting. If I am creating a leaf type, I don't need an ID. I don't want to give the consumer the ability to provide an ID because that would just give problems. I'm giving them exactly what they need to get through their operation. So I created a new detail for leaf type where they only get name and default deeds. They can't provide an ID field. Leaf type detail can be used for everything else, for editing, for viewing because well, it has those fields as well as BCE, which is the ID. Now, as you may end up getting more and more details and you have feels repeating across the different details, then you may want to consider having a common folder inside of that sub folder already to kind of define this property is that this detail must have anyway. So any leaf tab2 that will come up with most hafnium and it must have default days. You could define a base class if you need it to. Alright, now we can move on to the leave allocations have three leave allocation details at this point, and I'm going to point out a mistake I made in one of them. This mistake was to have creates leave allocation detail inheriting from this detail. All right, so you have to be careful about these things if we're trying to be strict. So that was a mistake on my part. Create leave allocation detail once again should have absolutely no reference to any primary key whatsoever. But then we do need the other fields. The updates, however, does need access to the base DTO for our updating purposes. Now, it could be that we said, okay, well, once again, create that base detail that everybody inherits from. Or we could just bite the bullet and say we'll use 14 create or update if the ID is provided and we know it's an update if it's not prevented and we assume is I create, those could easily be arguments I'm not seeing, no, I'm not seeing yes. It depends on how granular you want to get and these are your decisions to make based on your context. I'm just pointing out the differences and the risks accordingly. So I am going to be breaking them out like that. No For leave request goes through even another level. So we do have the leave request detail where familiar with that. We have the list detail which already established why that is different. We have the Create which once again is incorrectly, at least in my context inheriting from this, which I'm going to characterize null. And then I have to I have updated the Request detail different from changed leave request approval detail? No, you're going to ask. Okay. So why have both? Because both are talking about augmenting the data. All right. Well, once again, it's a matter of host tricked you want to be so the update serves the purpose of allowing the user or the consumer to allow the end user to update their requests. They wanted to maybe change the start dates or the end it maybe they chose vacation when this should have chosen sick. Maybe they want to put in new comments or they just want to cancel it. So I'm giving them exactly what they can do using this DTO if they send their request to update. These are the only things that can be updated. However, change leave approval. Request, sorry, leave request approval detail, sorry, would have only the ability to change. It's approved DSR null. So the approver is really just saying yes or no. I mean, if you had more fields like additional commands and anything that you could add them into DTO. Of course, they would have to be presented the domain objects. But my point is that I am using the detail here to help me enforce certain business rules and certain behaviors that my application is capable off. And, and once again, I have seen situations where there's one flat D teal and decisions are made in the 100 that based on the data present, maybe in the request are in the detail. This is the kind of operation to be carried out. So it depends on how much you want to break ETL to have that level of granularity. So you know, if it's an approval requested, know exactly which handler to go to. Or you can have one big command handler that is taking the data and a bunch of if statements to see if it's, if the ID is present, then if it is not present, then it, But then if it is present and this flag is not null, then assume it's an approval. You could do it that way, but I'm not doing it that way. I prefer to know that when it's an approval request, I have that change approval command. If it's an update, it's an update command and know exactly where to go to do what. So now that we have that detail explanation out of the way, I'm going to jump over to the features, folders in futures, in leave allocations and leave types at least I have already defined the new handlers and their commands. So let's take a look at the simpler one. So we have the update leave type command. And this command for the leaf type is just taking our requests are inheriting from Eric quest as we know, but then it's returning what we'll call a unit. So if you just hover over, you need to see that it's a mediator provided construct that represents nothing like void, right? No contents. So in API design, when you do an update, you'd usually return at 202, if I'm not mistaken, which is short for no contents. So it means it was successful, but I have nothing to show you. That's basically what unit is. And then we're taking that leaf type DTO as the property. I haven't implemented the commands just yet in the handlers, so we can do that together. I'm just showing that we have that same construct for the update leave allocation command, where we're taking the update leave allocation detail and returning that unit. And we have that command defined accordingly, also empty. So we're going to do those two together because those are pretty simple. And then we're going to spend some time exploring the leave requests and the business rules there. Now the workflow for our handler, and I'm going to start off with the leaf type command handler. All right, the workflow here is that we need to want to retrieve the original record to update that origin are required and then three, send it all to the database and then return the unit, or at least something to say it was successful. So two approaches. One, you can use the repository to have that specific method because then in the case of the leave requests, there might be specific methods. And then once again, you're going down that rabbit toy off. Having very, very specific method's in every single repository along the line. Or you can just do most of the work inside a handler, which is what is for anyway. Alright. So I'm just going to say var leave type is equal to leave type repository, Git. And then we're looking in the request, looking in the leaf to be looking at the ID. Now, another design pattern that I have seen is when it's an update leaf type command, they would use the regular leaf type, let's say the one that does not have the ID, but then put the ID property inside the request or the command object so that we, when you are making the updated to include the ID inside of this object. And then the detail is just itself. There are so many ways to do this. And once again, the ones you understand what you're doing, you can do me the best implementation based on your context. Here. Leave type is going to be retrieved based on the ID off the payload coming in with the request. And then we can see mapper not MOP. And then mapper dot map is pretty much just going to, in this situation, get the request thought leaf type D2. And notice that I'm not specifying, I need to type this time. I'm using parentheses, mapper dot map, open parenthesis. And then I'm seeing this is the source of the data. And I want leaf type that just gained from the database to be the destination of the data. And I'm getting that green line because I failed TO it on the line. I apologize. So there we go. So mapper dot map and then it seeing please just updates whatever is on the right with whatever is on the left, that might be different whether it's different or not, this, please update it because the update is going to send over. And that's why our detail needs to have as many fields that much the data, the domain object itself as possible. It's good. Dava name is going to have default days. We don't know what has changed, so that's why we're seeing autumn upper, just update all the values. If that Blanco the name, then we're going to update it to a blank name. Hopefully they didn't. However, if they didn't change name, then the expectation is that the same name is going to come back. So once again, we can't account for what might've been chin. So we're just seeing, please update the leaf type with the corresponding values coming from the object in the left. And then we're going to weight our neve repository update. And then this is where we send over our leaf type post mapping or sending it over to the Update. And then we just return unit dot value. And that's it. That is our update operation. Well, I have this red line up here because I have int and not unit. So after me that change, everything is okay. So that is it for obesity in the leaf type or what? The leaf type. So I'm actually just going to copy this. I'm not going to give myself too much work. I'm going to jump over to update leave allocation. And the only thing that we're going to be doing differently here is that instead of using the leaf type repository, I'm going to be using leave allocation repository. Instead of seeing leaf type DTO, I'm seeing leave allocation BTO. And instead of calling the object leaf type, I'm calling it leave allocation, everything else is pretty much standard and pretty much the same. And this will handle our update request. Alright, so I've gone ahead and completed the leave request commands for updating lacO, we see no that it's not that complicated or so I've gone ahead and done the update leave request command and corresponding handler. So our leave requests command handler taken from the command returning unit. And it is just knowing the difference once again, is the repository being used, but it is pretty much the same operation. Now one thing that you may want to consider is the specific business rules, our own this kind of operation. So yes, the code, it looks the same, but then when updating a leave request, there might be other things that need to happen. Right. So in the case of the change, request, change and leave request approval where we only get approved or not. We also need to set the action that we also need to probably update some other things. So I think you will be good if we had a specific function in our repository to handle the change request. Now the cool thing about requests is that we don't necessarily have to have our request per hour requests per all the time. I could actually reuse the same request and handler to handle this kind of operation. So let's look at this situation. So inside of the update, leave, not leave allocation, apologies, inside leave request command, we could have the leave requests DTO, but I could also have a property of type change leave request approval DTO. So this request is capable of having either one of these objects. Now in the handler, I can make a decision and call the appropriate method based on this because it's still an update command, right? So inside of this update command handler, I can put in a bit more logic. I can see if request dot leave request detail is not equal to null, then this is the route I wish to take. I can see else. And I'm going to speed it very explicit with this else because I don't know if maybe in the future I may have some other business rules. So I'm just going to settle for these two explicit situations where it's either that the this should be else-if apologies, else, if else if request dot change, request approval D2 is not equal to null, then do that, right? And then overtime was going to try and routine one returns. So I'm going to take all of this and say you carry out this operation when the leave requests detail is not equal to null. So that means whoever is consuming whoever's interrupting with this handler sending over their request needs to ensure that the fill. The appropriate fields according to what they want. Some just showing a different flavors because he could go the RequestHandler peering for every single situation, every single scenario, but then you can kind of bulk them together, like I said, the handler. Some people put a lot of business logic in this section. So once again, that's up to you. So if the change leave request approval DTO is not equal to null, we have a decision to make. What exactly are we going to do? We know we have retrieved and leave requests one way or the other. We're not going to do the comprehensive mapping our really want to do is eventually called the updates boats with other things happening. So I'm going to call awaits, leave requests, repository, change approval status. No. What what do we do? I could give it the ID. I could give it the leave request object, as well as the status that it should be changed to. There are a number of approaches that we can take. No. We always talk about repeating code, right? So let's say I do this, I go and fetch the leave requests. And then this would be relative to the change, the request approval details ID. And then I can say give this operation the leave requests that was retrieved as well. The value from the detail field for the approval, whether it's approved or not. All right. No, I'm repeating this call to get a leave request. No, I can only make this call relative to if it was the request the TOR changed leave requests detail that came in because if this is known, then I can't get that ID. And if this is null, so after make a decision. So you see all these other things? No. Okay, Let's refactor. So to make our life easier, we are going to go back to the command. And then I went to say, well, no, I see good reason to till the consumer include the ID. The ID must be present in these either way, but do include the ID here. So when you include the ID there, I can easily say request dot ID from outside of the if block. And then I know I have the leave requests that needs to be modified. Now, if it is that the whole detail GameOver, then fine. I know exactly what to do if it is just the change request. I don't have to go and find it specifically, but then I have it already and I can just pass in the approval status. So this method can get implemented in our I'll leave repository. And I'm just going to make it void. So it's just that task. And the parameters would be DV, request and a boolean. Well, it has to be nullable boolean of approval status. All right, so then when we call that, we're just passing in those two commands appropriately. So this is one way of writing the code. I'm sure you're probably sitting there and saying, okay, we'll probably could've done it this way or I need to do it this way for my situation, that's fine. But as long as we understand the flexibility that we have when it comes to that whole request handler pipeline and how we can handle different scenarios. We can use the one handler to handle the different potential scenarios based on the request, based on the data provided in the request. And, you know, you don't have to have peering for every single scenario that you might have, but you can have a handler to cover multiple scenarios. Alright, so now that you have the hang of it, I want you to go ahead and implement the delete commons and handlers. So I've gone ahead and done that. And we have the Delete leaf type command, which is inheriting from IO requests. So notice I don't have I request and datatype last time we use the units. So this is just the 0 that you don't necessarily have to put on that data type if it's not meant to be returning anything. So this delete command will have a parameter for the ID. And then inside of the handler, all we're doing is retrieving the appropriately based on the ID, and then we're sending it over to the delete and then we're just returning unit. And at this point that is the general theme for the mall. So it's the same thing for the leave request. Know whether or not you're going to actually expose functionality to delete a leave request. That's entirely up to your business rules because it might be that there is no hard delete, it's only a soft delete or delete really means cancel, right? So that would just it would just flag it that it was canceled, disregarded, but keep the record. So I'm just showing you how to put in the functionality. But then once again, the business rules and the application thereof are relative to your situation. So that is it for setting up the delete and update handlers for our domain objects. And pretty much that's the gist of the whole mediator pattern coupled with the C QR is sputtered. So as we go along, what we're going to explore is making this a bit more bulletproof because right? Know anything can happen, anybody can come and create anything, and there are no real rules to govern what is valid versus what is invalid. So we'll try and look at that whole we handle invalid data coming in and how we can make it a bit universal and foolproof.
12. Adding Validation: All right guys, welcome back. In this activity we'll be setting up validation for our details and our commands. Now before we move on, there's a quick carts on us I wanted to make for the code I wrote in the update leave requests command. I had inadvertently used the leave requests detail. So if it's in the update, then you should be adopted leaf Request detail. So if you caught that error and you cart did it to yourself, then kudos to you. If not, then you can go ahead and meet. This changes me. No problem. Now what we are talking about when we, on the topic of validation is the ability to make sure that the data that we're receiving before it goes into the database is well valid because as it stands, there is nothing here to stop us from committing, invalidate a toy database. And one thing that is very, very, very important is data integrity. So you don't want to create two records with vital data missing leave allocation. We don't know what leave type. It is still flat at that, so you'll want to validate it and then rejected of course, if it doesn't meet the standards. Now, when you're if you're used to MVC and do you think TBL validation and you're seeing that on the models we could easily put our data annotations, which is very true. I have found this to be useful. But then when you want to extend beyond the default ones, you have to start building old extensions and salt, which is, which is also very good. But then in this particular program, we're going to be using fluid validations, which is a library that allows us to use the fluent syntax and billowed very, very powerful rules and validation structures are owned our properties in our classes. Alright, so to get started, we're going to jump over to the new Git and I assume pretty searched for fluent. And you see all of these wonderful search results popping up. No point of information. The documentation for this library is very good and you can find it on the website for invalidation dotnet, and you'll see how we can extend this and massage it and use it to its full capacity to help with their validation needs. So I'm going to go ahead and install this library for the dependency injection extensions. And once that is done, we can close NuGet and then gets over to our setup. But one thing to note, I don't think I've mentioned this before. When you click on those CSB file, you actually see which packages are installed on their version. So alternatively, if you know the exact package name and version you want, you can actually paste aligned like this inside of your CSV file blob build and it will automatically get that package from NuGet for you. So that's a way you can also install, solve these packages going forward. So let us start with our validators. Know I wondering where to put these validators, I'm just going to collapse everything. So we can see all of our folders kind of compressed, alright? And then of course we have the details. The details are where our validations need to happen because they are the ones stuck seeing the inflammation on our behalf. We don't need to validate the ones that are being used for the queries. That's kind of useless, right? Because the read operations don't need validations. The right operations or the augmenting operations, however, do. So I'm going to, in one of the folders, let me start off with the easier one. I'm going to add and new folder. And I'm going to just call it validators. And then inside of this, I will add a new class. And then this one is going to be create leaf type D2 validator. Of course, to make it public. And then I'm going to let it inherit from an abstract validator. And then I'm going to pass in the name of the exact class that it is relative to. So I'll just go ahead and include any missing references. And then it adds that using fluent validations library and then we're ready to go. So what we have is a constructor. So CTR tub, tub and we get that constructor and then we can start defining rules. So let me just take a quick look at the create leaf type D2. What would we want to validate on this? Well, would want to make sure that the name values provided right. We also probably can limit the number of characters that this name property can have. You know, most of them didn't know, and it must have a maximum length. And it would probably want custom messages for situation for the default number of days, we can probably see that it's hostile, be more, more than one, it must be greater than 0, at least for the default number of days. So there are a number of things we could validator. So what we'll see is rule four. And then you'll notice that this looks just let the same lambda expressions that we're used to. And if you're not quite used to them, that's fine. We'll get used to them eventually. So it's rule for and then we can see name. And the cool thing about Flint validation is that you can chain things along. So you can chain it's along and say, Okay, this rule and that rule and that rule and that rule. So the rule for P dot name. Is, let's say, not empty. So that means it must have a value. And then I can say with message, so if it comes over m2, then we want this message to be printed. So I can do something like this property name so that we don't want to talk to. Type name must be your name is required, right? Because then I'm making dynamic. If we change the name in the class itself, then we may forget to update the message accordingly. So by just doing this, it will automatically inherit whatever name or whatever the name is off the property, that it is really too. Right. So we'll say property name is required. That's our validation message. Another validation we could put is not null. So we're letting you know that this should not be null. And I put my semicolon prematurely, so terrible that so it should also not be null. Let's see what those we can have. We can also say that the maximum length of the name property is maybe 50. There should be no leave type with a name that exceeds 50, right? And then we can add another with message to that with the message. All right, so I'm just giving you ideas. I mean, you may have different I'm requirements for your validation than I do, but these are general guidelines they can follow. So let's look at the, the p dot default is. So for default is you'd notice know that because the datatype is different, some of the validations might not necessarily apply. So I can't talk about maximum length with the default is that has nothing to do with an integer, right? You see that the arrows are gone, so I'm seeing it must be present, but then it's integer, so it will always, pretty much it will always be present, but we can leave that alone. But it shouldn't be empty. Then it can never be null. Really intuitive because integers are defaults 0 and null values provided. But then we did see That's it must always be greater than 0. And then I'm sure that there is a less than. So let's say we want to see in the system there should be no leaf type that is Putin that has any number of these Greta than a 100 or up to a 100. And it must be at least one. So the different datatypes can get different rules and we can chain them alone as necessary. We can put our messages accordingly so I can put my message here with message here. So with those validations in place on the leaf type DTO, let us see how we can go about making sure that these validations our own. So in the command that creates the leaf type, right, we do get that leaf, that BTO from our command object, right? What I'm going to do before I even do the mapping though, because I don't want to waste resources on an operation with invalid data. So I'm going to do the validation first. So I'm going to say var validator is equal to New Leaf type DTO validator. So while it's still a detail before I tried to even map it over to the domain type, I'm going to invoke that validator that I'm going to say var validation result is equal to. And I'll await my validator making the call to validate. And we have the async option, hence using the await. And then we pass in the object to be validated, which will be a request dots leave type DTO. At this point, the validation result is going to either have arrows are not. So I'm going to see if validation results dot is valid, so we get that it's either valid or not based on the rules. It's going to be valid or not. If it is. Let's say if it is not valid. And then for readability, I wanted to say if is valid is equal to false, right? Because sometimes in all fairness, when we just use exclamation sign, sometimes when you're tired, emit even miss it when you're reviewing the code and so on. So I'm just going to be a very explicit if is valid is equivalent to false, then I'm going to simply throw a new exception. So if you're used to exception handling, exception is thrown, that's it, basically crashes. The program will know later on we'll look at better exception handling and all of that can actually help us to write a bit cleaner code. Instead of helping a bunch of if statements to check a bunch of things, we just have exceptions. That's our being through and strategically to help the flow of the application. So in this situation, if it is not valid, then we're just going to throw an exception. We're going to look at how we can make custom exceptions also, which can be handled different from actual fetal exceptions. So that's pretty much it. We're adding validation here to make sure that it doesn't go any further. It doesn't go anywhere near the database. We don't want it to become a domain object if it is not valid. Now let's try probably the most complicated one. So I'm going to challenge you to settle validators for the leave allocation, which is really not that different. You just need to make sure that the number of days is present on It's more than 0. The leaf type ID cannot be null, it has to be greater than 0 also. And then you could be extended to make sure that the leaf type ID exists in the system. Because if somebody tries to spoof and Central Valley type ID that doesn't exist in our table, then that is also a validation arrow that we can actually catch before we tried to commit the database. Coming to the database, we can do that for the period. So I'm going to challenge you to do that one, but we're going to work on the leave requests together because this one is going to have a few more things in it. So once again, I'm going to add a new folder validators, and then let us start off with the Creates leave requests, DTO validation. What do we need to validate? Well, our dates need to be valid dates. We still need to get the leaf type ID, so we're going to do that together. And well that is optional, so that's fine. So let's go into that one. So it starts off with our class creates leave requests detail validator. I make it public. And then I am inheriting from the abstract validator relative to our Create leave requests detail. All right, so I've already written some of the quote for you and we're going to go through it. It's not completed because I want us to do certain parts together, but rule for start need to be less than the end. It's now we saw that we could also use the scalar value here. So I could have put an integer, but then an integer would be an incompatible comparison with the time. So I could have put date time, no. Right. Just make sure that the start date is not before today are mostly before today, which is not necessarily the case, right? So based on the business rule, you may want to compare accordingly, but this business rule states that the start dates must always be less than the end date. And then will the message I can say property name must be Before comparison value. So in our create leaf type detail, we had hard-coded the 50, we hard coded the, the one and the 0 here, but we could easily replace them and he spoke on parson correctly. We could easily replace those areas with comparison. I'll leave the one hardcoded for null, but I'm just showing you your options, right? So I'm seeing that start date must be before NDI it similarity for India it, it must be greater than the start neat and property name and comparison value. Alright? No, relative to the leaf type ID, I did say that our validation could take a number of forms. One, you want to make sure it is greater than 0, okay, fine. And all the more important one though, would be that you want to make sure it exists. Now if we just check if it exists, even if they sent over 0, 0 would never exist as a leaf type ID in the database. So we could chain it's along because then we could waste that database called by just doing that. So I could say greater than. And then I'm going to see 0. And then I'm also going to see it must exist. So I'm using what you call a delegate here, and I'm going to just erase all of this and retype it from scratch so you can see exactly what they're going to say most async, open and closed parentheses, it's a seeing, so we're going to await, but then we're going to define a delegate. Sorry, we're not that we're using, we're letting it know it's an async delegate which takes some parameters. In this case, we need the ID, which is the value. So we're taking that value as a parameter number 1. And then Tolkien is a cancellation token as parameter tool. And then I'm using a Lambda arrow to then define some object block or method block. So this method block is where we will carry out that check to see if it exists. Now you're probably wondering, okay, that means we need a database called how exactly do I call the database from just a validator? The cool thing about this is that it allows us to directly inject or dependencies. Alright, so we can continue by injecting our repository so we know how to injector it will put it in the constructor. We can use control dot to initialize the field. I already switched over to my underscores, which is optional of course. But using that we can inset of this most acing delegate function check for the existence of the leaf I repository. So two things to note here are three things. One, we're injecting the validator allows us to inject other dependencies like our repositories. That's one tool. We can actually have a custom function doing customized validation for someone to type this IN for it from scratch. So here we see a dot. Most I would have muslin the async. That's fine. So most async. And then because we're using async, we have to let the delegate model, it's erasing. So the delegate is when to take two parameters. Id representing the very ID that we're validating or the value we're validating. And tolkien would be representative of the cancellation token that we use are lambda R0 and then open and close curly braces. Then inside of these curly braces we have our logic. So the first line of our logic is to check or repository if the leaf type exists and then return that does not exist. All right, no, this function I just created, so I just extended our generic repository to have a method that returns a Boolean. It's called exists and it takes int id. So this, you can add that to the generic repository and you can use it across every one of them. But the point is that we can know, use that to check if anything exists in a particular table. And in this situation, it's a nice shoe in to check if or elif type ID exists. So know that you're equipped with hold to handle that leaf type ID. I'm going to challenge you to go and setup validators for the leave allocation. Hit pause, take a few moments, set up the validators Ford leave allocation and any other detail that we haven't looked at just yet. And then when you come back a more, just show you another way that we can refactor our code to kind of reduce on the repetition. Alright, so I hope that you actually took the advice that you went off, you tried to do to yourself and that you had some amount of success. That's good. But then I want to show you just another level that we can do this. So back when we were setting up the details, we kind of realized that would be ending up repeating our properties across multiple details. For instance, the create leaf type D2 and the leaf type detail, they actually have the same properties bar the font. Not one relies on the ID which we serve up through the base detail. So the validation rules for both of them will actually be the same except maybe the one that says the ID1 to the hub of validation for it. So because one has an ID, one does not, and the validator so far have been very strongly typed because when we have the create leaf type detail, it's only four to create leaf type detail and updates, D2 would have to have its own validator. So what I've done is to extend it a bit. And this is what they call pin driven development. It means do what you can until it is no longer practical, then you refactor, right? So when you're applying these solid principles, sometimes you don't see it right off the bat. But then at a certain point you realize that this is getting tedious or this is not practical, this is not in keeping with the principle. And so you refactor your code to get the most out of the principal at that point. So at this point, we're analyzing that we're having the same validation rules split across multiple files, which is fine. Or at least having multiple files for validations is fine. But having the same rules repeated can be dangerous because then if the rule needs to be changed in one, we could change it in one I'm missing the other. We know that risk. So what I have done is to have an interface which is an obstruction of our fields. All right, so like leave type detail I've created, I leave that detail and this has the fields that we know the leaf tab2 needs to have. So in the leaf type DTO, I have related inherit. I leave that detail. So these two fields are just the implementations of what has been defined in the interface. Just the same way in lift IB2, while it does inherit from base detail, it also inherits from elif type detail. So leave that BTO would have the ID as well as the properties coming over from our interface. Now. Okay, So, so the next step is that we can create an I leave type detail validator, meaning I am validating against the interface. So my rules are no longer directly applied to the leaf type DTO. They could be, that's fine. But then like we saw, we have to have multiple because we'd have to have one full leaf tab2 on 14, the Create. So instead I can set up validations against the obstruction. Both details actually inherit from the obstruction. So these rules will apply to both of them. And then when I have to get customized, I have my leaf, that detail validator in which I say create leaf. Of detail validator and everything that we know already. But then in the constructor I'm simply calling an include method. So this is fluent API is we are following us to have validators that apply to other things, apply to another class. So this really applies to the interface, what I'm seeing when I am doing this create leaf type detail validator, include the rules from the I leave type detail validator. And then I can have my costume methods also. So in the update detail, I could have the same kind of syntax, but then it's the updates, which means I also need our rule for. And then I can say I need a rule for the ID fields. I can say p dot. And then see it's giving me all of the properties, including ID because it's against that type. So my validation for the ID is that it should not be null and it should come with a message property name must be present because of course, when you're updating, you need to send the ID of the record that you're updating, which is why we do need that validation rule for the update and which is why I would have to have the separate file for the update. But this is much cleaner because at least we don't have to repeat the rules for the name and the type or the name. And the default is, sorry, we don't have to repeat that across both by the data. So you will see that actually did that already for the leave request and for the leaf allocation, once again, for the leave request, I have I leave requests DTO, and it's the same code that we just looked at when we did the leave requests detail valid data with injection. And in this case we have to initialize it and then we do the rules. But when we look in the Creates leave request detail validator, we see that we have to do the injection. So we still have to do our injection and we have to initialize it. And then we pass over that injection into the include method because of course, the IO request detail validator needs to have that injection. So Kant does call it we have to provide that value for the constructor. So it's just that daisy chain, but I think that this is much cleaner either way. And we don't have to end up repeating all of these rules right across the board. So you'll notice that both the create and update look very similar, except the fact that the update has that additional rule for the ID. And just for completion, we have the eye allocation details. I do have the ID tills, the interfaces didn't show that. So anything that is common across everybody, I just put that in the interface and then anything else can be put directly into the detail as needed and validated accordingly. But then for create an update, those are all the things that are we really need. We don't have to necessarily weekend sense of validation rule sort of request comments maybe limit the length. We don't necessarily have to do anything for cancelled. Once again, I'm just giving you the guidelines. Your business rules and requirements may be different, but you set up your validations as needed. So our update leave requests detail inherits from the base, and I leave requests detail. We don't have to do that for the list because we're not validating the list. We're not validating the detail detail either, but that creates definitely has to inherit. And then the change request approval that takes from this detail, but we're not quite at some point we can validate this. I'm not prioritizing it. But then over update and create, we definitely need to have already, all right, no, For our create a location, so I leave allocation and then both creates an update inherit from I leave allocation. So I'm just going to jump over to the I leave allocation validator where we have rules for the number of these. So this one is simple and as application grows, business rules change. We can easily put our validation here without modifying the custom queries and any other customers operations around said business rules. So rule for a number of days right now I just have it must be greater than 0. And my validation messages were a victim of some copying and pasting. So I'm just seeing property name must be greater than comparison value for the greater than rule for the period. So the period really should be the year, right? So for the period the year 2020, these were the number of these that you got. That's the pole points of the leaf allocation table in case that wasn't explained earlier. So the rule for a period is that it must be greater than or equal to the time dot 10 dot year. And we can bolster this a bit more about for now, we'll just use that. So we'll see message property name must be after this year. And then we all saw and wrote together the validation rule for the leaf type ID and no for the Create leave allocation detail valid data where simply injecting the leaf die repository and initializing it and passing it over in our include method. And for the updates were doing the same, except we also have that rule for the ID. So that is really it to validation. Yes, It took a while to get there. There were some reflectors along the way, but I'm sure you can see how it's all coming together to kind of reduce one repetition across multiple files and to kind of help too, keep everything structured. So one consequence of the following, the solid principles of course, is that you're going to end up with many more files which we discussed earlier. But it is coming together nicely and helping us to reduce how many times we place the same thing in multiple places.
13. Adding Custom Exceptions and Response Objects: Hey guys, welcome back. Last time we were here, we were sitting up our validations for handlers and for our various details. And in a nutshell, we realized that we needed to put in some rules so that whenever we get the create leaf type command with the create leaf type D2 or whatever detail. We can run it against the validator and then we would return an exception if it is not valid. So we should've done that across all the handlers for update and create anything that needs a validation should have minimum these lines. So I'll just click through and you can just go ahead and copy in case you didn't finish that up. So this is for the update. We just looked at the create for the leaf type update fully requests pretty much all of them look like the same thing. All right, they're all validating and entering an exception. Now, I want to talk about custom exceptions and MRD responses, right? Because at the end of the day right now, all we're doing is throwing an exception. An exception can be thrown based on our throwing it manually. It multi can also be thrown because of something else. He could be a database redirect problem, it could be something else, right? So it's always good that the consuming application or whatever is calling the handler has a good idea as to what through this exception. So the cool thing about exceptions is that you can extend them. So the base data type for an exception is the exception that we'll throw in here what we're going to create our own for the specific purposes. So we're going to start off by creating a new folder in our project. We're going to call it exceptions. And in it we're going to have bad request exceptions, not found exception and validation exception. So you can go ahead and create that folder. And these three files, remember to me them public, of course. And what we're going to do is let each one of them inherit from application exceptions. So exception is the base type of an application exception is used as a base type for application defined exceptions art. So we're just going to go ahead and let each one of our classes inherit from that butter requested exception will later on when we want to define that the requests that was central versus bad, alright, but for now we're going to initialize it or read the code to wire it up. So all of them will have a constructor. And for this one, the constructor is going to take string message and then our base has to inherit the same message that is passed the base being our obligation extension. So that's what that exception looks like. Application exception rather. Sorry about that. No, Moving on. We can do the same thing for our NAACP phone, but then we can be a bit more explicit with certain things. For instance, if we're going to be seeing not found, we will probably want to see the name of what was being sought after and maybe the key value. All right, So we're passing in Name on keyboard, then the base requires string, so it has three overloads. Want to pass in a string here. So we can just pass in our message that we know we want to print. And my message is going to say that the name, whatever it is and its key was not found. All right, so if the search for something, it's not formed through not found exception, for the validation exception. We're going to get a bit more fancy. So the validation exception is going to want to R, we're going to want it to return the list of all the things that were wrong with the request or the data that will send over in her quest, right? So I'm going to have a list of string and I'm going to call it errors, right? And then in the constructor, we're going to have the validation results, the data being passed in. So validation results of validation result is coming from fluent validation. So we'll just pass that whole object in there it is using fluent validation results. All right, and then we can see we can initialize or errors. I mean, just initialize that here. And then we can see for each validation error in the errors. Or I can just shorten it and say for each error in validation result dot errors, we want to add that error. So I'm just going to say urls.py. And then we just error dot and we have an error message. There we go. So that error message would be whatever we had setup in our validators as the message to be returned when it is not valid. So now we have our custom exceptions. We're really going to focus on the validation exception, right nodal. And so in our handlers we can actually update this from throw new exception to throw new validation exception. So if the validation result is not valid, we throw this new exception and we pass in our validation results and you want to include any missing references. So you see here it's asking for these books. We know we have it defined. In our custom exceptions. All right, so you can go ahead and update each line that was previously just throwing a new exception to know throw that validation exception. And please remember each time that we're including our custom exception and not the fluid validation or the data validation on art. So go ahead and update them all and make sure you're including the cart library. Now at this point, I can imagine that you're wondering, Okay, So how can I use the other exceptions? Well, let's look at the not found exception. So in C, the delete operation, we have to find the record, then do the delete and then return well, the units, right, but then what if we don't find that record? Well, then that's a perfect opportunity where we say if the object that we're looking for returns null, or if the operation returns null, then we throw the not found exception. And what would we pass into the not found exception? Remember that's it takes two parameters. It takes the name and the key, so we could easily say name off. And then this is a nice way to keep it strongly typed. So we're looking for a leaf type. So name off leaf types or say in the leaf type with the id that was passed in was not phone or other requests. Dot ID, right. There we go. All right, so then you can start decorating your Delete handlers with that one line. So in the case of the leave requests, that will be the same thing except we're checking if the leave request is null, and then this would be a leave request not phoned. Go ahead and update. So you can do that with leave allocation also. All right, so once you're all done with that, then you have settled some nice exception handling at least or custom exception handling in your handless. Now another thing that we wanted to look at is customer responses. So what happens when there's a positive result? And even when there's a negative results, right? So this is where your, your architectural needs may differ from mine in terms of what you want to do. But here's a concept. We can define custom response types or have base responses where we can return data based on the situation. So if it fails, we could throw the exception sure. Or we could have a customer response that has a success false flag, it contains the validation errors. And so the client will always know that I am expecting a response of this data type that we should always have this data. If the flag is false, that I know IT field, if it is true that I know it pass. So we're going to look at something like that. So kind of an alternative to just throwing an exception or returning just the ID, what we could do is define a new folder. So we have a new folder here called responses and inits we have a file-based command response. And it will have three properties. Success, which is a Boolean message, which is a string, and a list of errors, should we need to send back the errors, right, so then after we have this base command response, we can extend it to facilitate the specific operations. So for instance, one result will probably want to extend this is that we want to return certainty to each time a leaf type is created or updated, right? So the base response might not be enough. So we can create a custom response associated with leaf types. So we already have those web requests. Then you could create another folder called responses, and then we could extend that. I'm not going to get that complicated. However, what I'll do is just add another property here and I'll call it ID, right? Or we can call it record ID. So that means whenever something happens, we create, instead of just returning the id, what I could do is, and this is a huge change. So let's go through it line by line and I'll address the red lines as we get there. So initially we were just saying get me the validation results, throw an exception, otherwise continue and then return the lever. Leave. In this case, leave request ID. Alright? Know what I'm doing is I'm seeing firstly, initialize the response, so we have a base response, that's fine. Then I'm seeing if the validation result is false, set those spawns thought success to falls. The message you can put in a custom message if you wish. And then the errors we would like to fill with the same validation errors. So I'm just selecting them from the list of errors are from this collection of errors. So this select has a red line because I need an extra library, which is System.out links. So just make sure that we could see that together. And then it just gets the error messages, puts them in a list, and then that goes in. So that's a nice one liner itself the for each loop right? Then later known, we see if the ad was successful and soul, what happens is that if this feels unexceptional would've been through an automatically by Entity Framework anyway. So if this fails to happen, then we get an exception. So it would never get this far if this was not successful. So then the success is true. Our response is creation successful, and then we set the ID. So that's what I'll see you in that across the board, we're only returning IDs, right? We were only returning the ID for the newly created record. You could have a requirement where you need to return the entire record. At that point, you could just extend base command response creates a new class called it creates leave request response. And that it didn't hurt from this and give it the DTO parameter for a leave request DTO, you do your mapping, you return it. Like I said, I'm not going to get that complicated, right? No, we can't look at that in our future considerations or additional considerations lesson, but I just wanted to get this concept of a customer response across. So then we return the response. Now this has our red line because we had defined our command to return int. Alright, so we can go over to our command. Let it know that I request is supposed to return the beast command response. All right, and then our error will appear and we jump buck and were bucking or handler, we see that we can return response as soon as we also update the handler, right? So remember that we have that command and return type peer-to-peer. So let me just update that. And then finally the type on the task for the handle. Alright, So there, so when you want to change, alter your return divs, that's all you have to make all those changes once again, right? No bs command response. So if you want to get granular, I keep on saying you don't have to get as granular book based on your requirements, you will need to are you may not need to. I am not making it a requirement to gold and create command responses for every single solitary handler or command handler that I have. Right now, I'm just going to be using the base response. And I'm also going to be making that change to the leave request for now at least so that you get to see the idea of how you can have custom exceptions and, or how you can improve on that top of your custom responses.
14. Additional Refactoring and Considerations: Hey guys, welcome back. This is more of a review session than a coding session and just some additional considerations. So throats or activities may have mentioned that you have alternatives and you always have alternatives. Whether these alternatives are good or bad or quote unquote, best practice sometimes is relative to what you are doing and what you need to accomplish. That being said, of course, there are foundational principles that you want to adhere to regardless and the ones who will have those as your guide, then you are more than likely going to make better decisions. So one thing that we want to look at is the separation of concerns, right? So separation of concerns led us to have multiple projects and far more files than we probably had in other projects as I'm ensemble fact, I think that we already have more files in this one project than we did in the entire application that we're both to rebuild, right? So just in this project alone, we had multiple DTLs. Why did we have multiple details? Well, one we wanted that kind of separation because there might be business rules that govern what can occur on any type of operation. So let's look at me leave requests details. Leave requests details. We had one for the listing which had only the data that was absolutely necessary for when we need to serve up the list of leave requests. We have a DTO that has all of the fields that match what's in the table. And this could be seen as the detailed DTO. We also had the update, which only had a few fields required for an update operation. We had the Creates where we had few fields for a create operation, et cetera. So we split them out into multiple files. So that is one of the I wanted to say consequences of adhering to those separation of concerns principles. But you're going to end up with far more fuzzy than you're probably used to. And you're going to have to separate them in a way that you can always find them. So I started off with details then in details, I separated them by the type. And then after that then you start seeing the files will also set up the validators. And then because of the different validation rules that might be required for the different DTUs, we have multiple validates, a soil one for Create, we have 14 update. But then at the same time we saw where we needed to kind of consolidate because it was kind of overbearing and we started repeating ourselves. So you have the DRY principle. Because of the DRY principle, what we did was we created an interface that had the base fields. So let me look at maybe leave allocation will be a better example of that. So we have, I'll leave allocation, which has all the fields that are necessary for annual leave allocation. Then we have our details inheriting from this interface, right? So yes, you see the implementation again or you see the fields up here. And again, what they're really being driven by this requirement of the inheritance from the interface. And then we could set up one validator against the interface. So all the common fields, right, settled by the interface are being validated. And then the other validators are just including the validations and then implementing their custom validations as needed. That helps us to reduce the repetition of code. And once again, as your project grows, you run the risk of forgetting to update one part of your project when you make a change somewhere. Know another thing to note in our validator, initially, initializations in our handlers, and I don't think I pointed that out earlier, someone to make sure I do know is that when we're initializing these validators, we have to pass in an object of the type that it's expecting, right? Because remember that this validator needs the I leave type repository and then we have the constructor seeing I need the elif type repository. So there's no way to instantiate this without passing it in. I like we have done here. So if you had directed line and this entire time, I apologize, I overlook that one, but you can go ahead and do that because what you need to do is injected into the handler and then having injected it into the a 100 and you just pass it along similar to how, when we need it to include the base validator, we had to do the same thing. All right, this is the same principle, dependency injection. All right, so I hope that cleared up an error and if you didn't have that error, then kudos to you. Now another thing that we neglected and we can address know is the inclusion of all the details that are required for any mapping operation to be successful. Right now we only have mappings for the like, for like request the teal to the list are these are the only the domain to the detail detail. So of course we will need, once we're doing mapping, like in this handler, we are mapping from the Creates leave allocation detail to leave allocations. So that means we have to have representation of it inside of our mapping profile. Here I've added the additional mappings, all of those for leave requests that I like to group them so that they don't get mixed up all over the place. And all the idea of grouping is a can probably just create our reason, our own the sections so that you know exactly where which ones are, which set starts or town you can call up such application grows. You may want to do something like that. Alright, so that is another thing that we definitely need to address before we move on. One of the thing that I want to 0 star talk about is our folder structure. So I had mentioned multiple times that your folder structure may differ based on your temperament or your Outlook or your visual. And four hold these files need to be arranged, right? So I like to think of see QRS or the implementation of the seahorse, our own scenarios. And then all scenarios, we have particular assets that are required. So when I say is a scenario, I mean, creating a leave allocation, that is a scenario what is required to create and evaluation? You need your command handler. You'll also need your command object and you need maybe some other details. So you may want to create and you just include, you may want to create a folder inside of leave allocations that has maybe instead of features you would have created leave allocation. And then you have your handlers and all of your assets inside of that one folder. So the folder structure can differ as long as the organization is such that you can find your assets when you need them, then you are on the right path. Now while I am in the Creates leave allocation command, another thing that you could consider, so you see there are so many considerations, this is not set in stone, right? Another thing you could consider other is that inside of the command, it is a well-known pattern to just use the command object for your fields to carry out the commands. Instead of having a whole DTO inside of the command, you could actually put the fields from the detail inside of the command. And then you just validate the command itself, the request itself, the object coming over into the handler would just be this object. You wouldn't have to say request dot, leave allocation, dto dot this field, you just say request dot this field, that field, etc. So there are a number of options available to you, but I'm going to stick to the details with all that said and done by way of conclusion, the core or the application project as we have it, contains the core functionality of the application. So you see that everything is abstract at this point. We're going to move on to the next module where we're start putting in some real quotes, some meat into the repositories and any other logic that is applicable. We looked at how the mediator pattern works that promotes loose coupling coupled with the C QRS pattern where we can know exactly which file is doing what. And this handler is going to handle this behavior, this scenario. And we can expect this particular response because of that kind of relationship or behavior that we have implemented. We also looked at features based layout, which in my opinion helps you to see that, okay, the feature related to leave types, you can find everything inside of that. And that to me that helps with the layout. You may have other ideas, but this is my recommendation. And then we looked at validation using Fluent API or fluent validation. All right, so those are the things that we've looked at in this module. So when we come back, we will definitely kick it up a gear and start putting in some more functionality.
15. Section Overview: Hey guys, welcome back. In this lesson, we're going to start our module where we set up our infrastructure layer that you're probably wondering, okay, what is the infrastructure layer? Well, one, it's going to sit inside of this folder called infrastructure. And two, it is the project in which we're going to actually implement all of our obstructions that were defined in the core section. So we'll be setting up our database contexts that we'll be using Entity Framework Core as our ORM to communicate with our database under our application. But in this layer, you setup everything is settled. The logging you, we are going to implement the repositories we're going to put in the actual meat, the application. So let us get started. So let's create two new class libraries. One called HR dot DV management infrastructure and one called HR dot leave management thought persistence. Know the persistence project or persistence layer, will deal with our communication with our database. So this is where our database context or EF Core libraries and references, all of that. Was it there instead of infrastructure, that's where our implementations will sit. All right, so between these two will be implementing the repository. We'll be setting up any other third party implementations that are needed. And we'll also set up services to be bootstrapped into the services call it AI services colleagues Sean or the dependency injection container for ASP.net Core. Remember that libraries need to be done. It's thundered. 2.1. And you already went through creating all the collapse of diversity can follow the same steps. And when we come back, we'll start off with setting up into different work core in or persistence layer.
16. Adding Entity Framework Core: All right, so we're back and we're going to be setting up our persistence projects. So let us get started by adding a reference to our application projects. So we have the domain project which has all of the entities. And end application project has a reference to the domain project. So our persistence project is going to have a reference to our application project starts so we can just click application to equal k. And then that's one dependency. Don't we're also going to go over to NuGet packages. We're going to look for Entity Framework, but the one that we're going to be getting is entity Framework, Core dot SQL Server. So this one when it comes down, will come with all of the dependencies that we need. So we can just go ahead and install the latest stable version. And still in you get, let us just go ahead and search for configuration extensions and install Microsoft dot extension's not options dot configuration extension. So this will come in handy when we are going to be sitting on some of our stuff. So after we've done all of that, we can go ahead and create a new class. And I called mine the age, our lead management BB contexts. So create that new class. You can call it something else. You can probably just call it leave management, EB contexts or DVI contexts, that's fine. But it will be inheriting from BB contexts now DVI contexts comments to us courtesy off microsoft dot Entity Framework Core. So we can go ahead and make that reference. And then we can make our DB context aware of the different entities that we had defined. So if you're familiar with EF Core, then you know exactly what I mean. So in this file we have a few things. We have a constructor where we initialize our DB context to have a parameter of DVI contexts, options off its own type, and the name is Options, and we pass that on to the base, which is DVI contexts. Then we have our DB sits relative to our different entities. So I can just go ahead and include the missing references for those and everything should be all green. Next up, we're going to override a few methods. So the first one that we're overriding would be the on model create team. Actually it's quicker to just start typing over it and then you press Space and then you'll see all of the options. So we're overriding the on model creating. So this method gets executed whenever the database is being generated, right? So we can sit up certain rules. So the rule that we're going to setup here for sure, at least, right? No. If we wanted to see the database, we can do it from here, but we're not ready for seeding as at least not yet. We want to apply configurations from assembly. And then we're going to say type of DV management, DV context. And I'm going to say dot assembly. Alright? So that is all we're putting in our model creating at least, right? No. Like I said, if we wanted to seed the database with spatial configurations for tables, then we could always inside of this method so they get applied whenever it is generated in the database model. All right, another thing though that we want to override foreshore is our Save Changes. Someone to choose to save changes with the cancellation Tolkien as its parameter. And I'm going to outfit this save changes with some beautiful code, some nice handy code that's going to allow us to do some audit logging automatically. So remember that we had set up a base entity for each of these that came automatically with like a user-created, are created by or other, creates a data, et cetera. So I'm going to set the foreach loop to go through each entry in change structure dot entries. And we're just doing an implicit data cost based domain entity. And I can just go ahead and include the using statement for it. And then for each one, what I want to do is set the date, added, date, modified at all time. So I can always see once you are about to see some change, I want entry dot entity, dot glass modified by our Last-Modified data, rather, we're dealing with the date, right? No. Time. No. Right. And then I went to do a check and I'll just say if the entry state is equivalent to entities, State DOT added, meaning it's beats being added, it's a new record. Then we would want to sit the created it to datetime dot null. So we allow is our date created rather, so we'll always set the Last-Modified. It wants some things being changed. A voltage we're sitting modified it. But then only when it's being added to, we set the created it and that's what that's like. The most basic of basic code to implement auditing that you may ever find. Once again, this is automated, so every time we hit Save changes, it does all of this. And then it just caused the base c of g and g is method in the background. So that's it for adding into the framework to our persistence layer. When we come back, we will start working on some implementations.
17. Implementing Persistence Layer: Welcome back guys. In this lesson we're going to be implementing our persistence layer. So when I talk about implementing the persistence layer, I'm specifically referring to our generic repository, right? So we only have the abstraction, but we have no code to back this up. So let's go ahead and do that. So we add a new folder. And I'm going to call this folder repositories. And then in this folder we're going to add a class that will represent the implementation of the generic repository relative to type T. Cells degenerate in the class. We as usual make it public, and then we make it relative to t0. And then it inherits from IJ generic repository, which is also relative to T where T colon class. All right, go ahead and include any missing references and then allow it to implement the interface. So I'll go ahead and write the quote and then we'll go through it together. Now before we continue, I realized that I got over Zillow, Switzerland for copying and pasting phone. We're sitting up the interface. So just as some cartoons for the update and the delete, we can remove the T, right? We don't talk to return anything when we do an update or delete. So those two should only be tasks, so you can go ahead and make that change. And then that of course, will affect our implementation. So our generic repository starts off with a constructor that is accepting a parameter of type leave management DVI contexts. Of course, the DB context is basically our connection to the database. So we do need it in our repository in order to carry out our operations or add method. It starts off with our weighting a DB contexts call to add a sink where it just passes in entity EF Core is intelligent enough to infer what entity is being passed in relative to all the DB sets that have been defined in our DV context. And by db sit I mean these. So whatever datatype is passed in, it will know if it is one of the DB sets That's it recognizes. So we'll just go ahead and add the entity, save the changes on the new return, that entity. For the Delete, once again, remove the type parameter from task. But all it's going to do is look and DB context, find the set relative to t0 that it is being given and it's going to remove that entity from that sit. All right, and then after that it saves changes. So you'll notice that nothing really happens until you see if changes. This is that final commit to the database. We have the exists method where we get an ID for our record. And what I'll do is look for the entity using the local GET method, which we'll look at in a few. And then we return that it is not equal to null. So when it's not equal to null, then yes, it exists otherwise that's false. Of course, in the ghetto, what we're doing is we're returning an unweighted call to DB context dot set t. So once again, we're looking into specific set and we're finding the record relative to the ID. All right, for our I read on the list t, which is gets all that's what it's returning. All we're doing is looking in the set and we're doing a two list async on that set. So we're just getting everything from that set and sending it to list and returning that in our guitar for the updates, what we're doing is setting the entry into the state to modify it so that the EF Core, we'll start tracking it and then we go ahead and save changes. So that is pretty much it for our implementation for the generic repository. All right, so now that we have our generic repository implemented, we now need to implement our specific repositories so we can go ahead and add those. So starting with the leaf type repository, we're going to look at what the implementation looks like. So I've created the leaf type repository. It is a public class called leaf type repository, inheriting from the implementation of the generic repository relative to the leaf type. All right, and then we go ahead and say it is also to inherit from the elif type repository. So the red lines here indicate a few things. One, we need to bring in the missing namespace to, we need to put in the actual implementation this, this swan, right, this interface didn't have any additional methods, so that's fine for null. And then this is complaining because we need to have the DB context present. For the generic repository. So remember that when you're inheriting something that has a dependency, you have to put that dependency in the inheritor also. So that's a simple solution. We just get the constructor and then do our dependency injection. And that is for the DB context, of course, but then we also need to let the bass note that it can also use this DB context that is being injected. And that's it for the leaf type repository. So the contract didn't have any additional methods. There's nothing extra to implement. And because it's inheriting from the generic repository relative to the leaf type. By using this implementation, we have access to all of the methods that were defined here. Now let's look at some more complicated ones. So let's look at leave requests. Leave requests repository without this seem dependency requirements. So we have to make sure that we inject the DB context possible to the bees. Go ahead and include any missing namespaces, and then we have to implement the interface. So this interface actually had a few methods, extra. We had change approval status, we had get leave requests with details, and we add another one to get leave requests with details by ID. Few things are happening in this particular one. So the implementations here will be different from the generic ones because these are specific to some leave requests related operations. So the implementations are as follows. For the change approval request, we're getting a parameter of leave requests and approval status. We're setting the approved state of the lever quest to whatever value it came over and the parameter. And then we're setting db contexts not entry leave requests. So you see this time is not an entity, it's not generic. It is very specific because we're in this specific repository. So we're setting the entry state or the entity state, modify it for that leave request, and then we save changes so it will start tracking it and then see if the change accordingly. Now you see that this is a very specific operation as opposed to the general update where we can't account for what is being changed. So we just set everything to modify it and allow it to truck. Once again, these are just ideas because maybe your business rules are far more complicated or the operation you have to carry out inside of the repository is far more complicated than just sitting one field. So you may need a specialized function for that. Now for the get's leave requests with details method, all we're really doing is querying the leave requests table. So var leave requests is equal to await DVI contexts not leave requests. And then we're including the leaf type. So we don't necessarily always want to include the leaf type. We don't know under what circumstances we may need it. So we have this one relative to the details that are needed alongside the record for the regular generic one that is not including is just returning data from the table. So this time we're including details. It could be this leaf type, it could be more as many includes As you may need. And then we're pushing them all to lists. And then we're returning for the final one where we're only getting one leave requests with details based on the ID. We're doing something similar, except we're doing the include and then dx in the first or default were the queue ID matches the ID Boston. Now we're doing first our default here as opposed to the find, a sink that we did in the generic repository because of how these methods work, you can't do an include when you do a find, it just doesn't work. So when you have to do an include, you have to use the first or the single or default, whichever one you feel more comfortable with. And then we return the leave request that has been found. So we can jump over to leave allocation and we see that it's a relatively similar implementation. We have similar methods. So I put these methods in here just to point out that you can have custom methods in these repositories. You may not necessarily need them in your application, use them as you need them. I also retain that red line because in case you're getting into all you need is to include that reference, that EF Core and then that is good. All right, so we have one major activity left and then we're done with the persistence layer. And that is to set up the registration class for the persistence. So just local application services, Hadar distribution. Persistence will definitely need one. So go ahead and add this new class. I'm calling persistent services or distribution. It should be a public static class. And then in it we will have. A method that returns I serve as collection and we're calling it configure persistent services. So of course, as usual, go ahead and install any or either other Any using statements that are needed. And then inside of this method, what we're going to do is write code to wander. You Store DB context, and add our repositories tool, the service collection. And this is that full method of, of course, what the red lines because always love to show you what Redlands me a exist and how you can solve them. But this is what this method needs to look like. So you can go ahead and start including in the using statements that are missing. So firstly, we have the services.js ADB context where we pass in the type that is the db contexts leave management DB context and options dot SQL Server. So remember the DB contexts we told it that we need to, in the DB context on model creating, we told it that it would be sorry, in the constructor, right? We took BB contexts options. So those options are really coming from whatever options are defined here in there, a distribution. So we go ahead and use EF Core for the use SQL Server configuration is coming in because we are going to be passing over the configuration from the client application that is implementing this called the services bootstrapper. And so we will definitely need to pass. That's a no conflict. I configuration needs to be Microsoft extensions dot configuration, not autumn upper. So be very careful with that one. So we can go ahead and include that namespace. And then for depositories, we have add scoped. So we're adding all of them are scoped. But then for the generic, notice that we're adding type of ij generic repository with our angle brackets comma type of generic repository, angle brackets. But then every other one is the interface to the implementation peer without any angle brackets. But I'll go ahead and add any missing references here. And with that, and oh, I'm sorry, I wrote the code wrongly here for this one, add scoped is open parentheses and no angle brackets for the ad schooled. All right, so let me just cart that so you can see. There we go. So it's scoped parenthesis, type of eye generic repository with angle brackets, comma type of generic repository with angle brackets. I don't once again, those are in parentheses while the others will have the angle brackets surrounding the interface and implementation peering. After all of that, of course we return services. So just to backtrack a bit to what this connection string will be, we don't have an app settings yet, so this is going to live in the settings of the application that will be calling this method. And so when it is calling configure persistent services, he is expected to possible with that configuration object, which will then give access to the app settings. And we will be able to get that connection string to possible What's our DB context? So that is what that whole line or that whole section is doing. Now the purpose of force using scoped, there are three injection models available to us. We have at school to have singleton and add transient. Singleton means at one instance of this service will exist, throw the entire application. This can be dangerous based on the nature of the service. And it could be useful, for instance, maybe a logging service that could be one in sensor or the entire application. You probably don't want something like that for your database transactions. Because then when you have multiple database transactions, you kind of want those to happen in silos. So that's why it would use scoped, which means that for the lifetime of our quest, I am requesting something. I am writing to the database, I am calling your service for the lifetime of that operation. Then add scoped will invoke and connection to the database or an instance of this leaf type repository or any one of these repositories, as it is called on during our request. Once I request is finished, it will no longer be in memory. So that reduces the chances of conflict. And then add transient means that every single time it will always do something new, which means that you might end up with more than you really need to complete a request, which could also lead to conflict. So once again, you can use them sparingly, but within this context, add scoped is the one that we want for our database related operations. Now with all of that done, let us do a quick bill to ensure that we are right on track and that we have no errors and we have successfully built our projects. So when we come back, we'll start implementing or infrastructure.
18. Add Infrastructure Project (Email Service): Welcome back guys. In this lesson, we'll be setting up our infrastructure projects now our infrastructure project is pretty much where the implementations for all our third-party services will sit. In this lesson, we'll be implementing an email service with the help of SendGrid. And we're going to be setting it up in the infrastructure project. Now the first thing that we need to do is set up an abstraction for the e-mail senders, same way that we had obstructions for our repositories. We have an obstruction our contract for the email service. Now, on that note, I was looking back and I realize that the folder structure here is not very intuitive for the long run. So I went off to refactor here because I have persistence then I have contracts. But contracts is the more universal term because then you have contracts for the persistence in their contracts for the infrastructure layer and so on. So I'm going to have to flip. These folders are owned, which won't be too hard on this called persistence folder contracts. And then I'm going to call contracts persistence. Now this is going to have a ripple effect for all of the namespace is because we do want our namespaces to be completely representative of what they really are. So we have persistence dot contracts, no, we'll have to change that to contracts thought persistence. And then this is going to miss with all of the namespace references. I'm sorry, terrible that. But it's a factor that is definitely required for us to have intuitive folder structure, right? So after doing all of that, if we do a build, it will point out all of the butt namespaces that were referenced throughout our project. So, you know, quick way to go ahead and fix those. But namespaces would be to look for persistence dot contracts everywhere and then replace it with contracts dot br system. So you can just do that and meticulously go through and replace each term does to me sure you don't override anything that might be important. And once you have exhausted that search, you can go ahead and do another bill just to make sure that you no longer have any build errors. And once that is done, I'm just going to close all tubs and that were opened in that operation and then we can go again. So in contracts we want a new folder, our calling this one infrastructure. And then inside this folder we're going to add a new contract or interface. And I'm calling it I email sender, know I email sender is going to be public interface with a method that is going to be called send email. And it will take a parameter of type e-mail called email. Now we need to define what this email looks like. So I'm going to create another folder for models, right? So let me just add that folder. We're calling it models. And then inside this model's folder, we're adding a new class that we're calling email. So this is going to be our template, our model for what any email should look like. And then this e-mail will have the typical properties of any e-mail that to the subject and the body, all string properties. So now that we have that model defined, we can go ahead and add the using statement and have that sorted out. Before we move on from our model definitions, we have another model that we need, and this one is going to be for the e-mail settings. All right, so it's going to hose properties for an API key from address and the from name, all of which are string. Now that we have a few things fresh though with our email sender, you're probably wondering, okay, why do I need e-mail? So then puts it into context because we just started building of the email service with no real contexts. When somebody applies for leaf or maybe their approval statuses change something like that, you'd want to notify them that this action has taken place. No weirdo actions take place well relative to features, we have our handlers. So when you create a new live allocation, okay, maybe you don't need to send an e-mail, but then a leave request would warrant an email to be sent whenever one is created or one is updated. All right, so let us look at the Create leave request handler where we can simply inject or I email sender. And I'll just do that using the IntelliSense. Quickly, initialize the field. And then given our naming convention, I'll just rename this using the underscore inside our handler after everything has been successful, before we return and kill the whole operation. So the handler is still going on up onto this line. So will appear an email object and then try to send it off. And we'll deal with an exception. So let's look at this slowly. So var email is equal to a new email, which is our model that we just defined. We have the two, I just put in a fictional email there when we get to the whole user authentication and having actual users submit. And we'll look at how we get the actual email addresses. We have the body. And the body of this email just says your leave request for and I'm just putting using interpolation to put in the content start need to end. It has been submitted successfully and the subject is leave requests submitted. If you want, you can further adjust the date by putting on colon D. So traditionally you would say toString and then specify the format. But when we're using interpolation in this version of C Sharp, we can just put on colon and the formatting, well, the formatting string at the end of it. And that will take care of it for us. So d would give you the long name. Monday, June this date, this year. All right. So that's it for or handler know that our whole application knows about the eye email sender we need to work on the implementation so that implementation of this will live in our infrastructure project. So to get started on that creates a new folder inside the infrastructure project called meal. And then create a class in there called email sender. And then this class, which of course these are the public will inherit from i e mail sender. So we can go ahead and then you realize that it needs a reference to the application. So we need to go ahead and add that. And with that reference at that, we can go ahead and implement the interface. So before we move any further, we need to jump over to new Git. And we need a few packages. Swan is the same configuration extensions that we would have used from the other projects. So a quick way to kind of manage common projects is to just go to this solution and say Managed NuGet packages for the solution, right? So we already have some packages installed in some projects that we need in others like this one, the configuration extensions. So I can click on it and I see that it's already in the persistence projects, but I wanted in the infrastructure project also I can tick it, click Install. And so that can reduce the amount of time you spend on NuGet trying to find the same package over and over. Now the next package that I am interested in is SendGrid. So you can jump over to the bros and just step in SendGrid, and then you can get that latest versions. So you'd want to click it, make sure you're taking the correct project and then click Install. So after that is installed, we're going to start wiring up this class. So one thing that I'm going to do is setup a private field of type e-mail settings and upon its underscore e-mail settings making read-only. But then I am initializing it in the constructor with this i options Email Settings parameter. Now let me explain what this is similar to how we had setup our database. And we said that we have an app settings file that we'll be providing the connection string when the time comes. It's the same way that we can actually pass over. Options are chunks of options are configurations from the, from the whole app settings file, which can be then deserialized into a whole object for us. So what we're doing you seeing get me from the, from the options are the app settings, the e-mail settings, JSON equivalent, and send it over as this parameter. And then we can just inject it in and then have it as our local variable, our field in our class. So this dependency injection is so cool because it makes everything so loosely coupled and malleable. So let us continue with setting up our Send Email methods. So the first line that we're going to have is a client that is going to call or initialize a SendGrid claim. So we're just going to go ahead and add in using statements that are missing. Then after we get the client, we're going to have to get all the subject and the two and the e-mail body from our e-mail object. But the two and the from especially need to be special datatype. So I'm going to say var 2 is equal to new e-mail address. And e-mail address here is coming from sin grid. So new email address. And then we're going to have to pass in email dot tube. And then we're going to have to do the same thing for the from. So I'm going to say var from is equal to new e-mail address. And in that one, we're going to kind of put you in a bit more in the definition where I'm going to see the e-mail is coming from the e-mail settings dot from address. And then the name would be the e-mail settings dot from name. Alright. So we have the from and the to defined. And then after doing all of that, we need to see. Message is equal to male help her. So Male Helper is, that's going to be a static class given to us by sin grid that allows us to create single email. There we go. And you see you have different options to multiple recipients and multiple e-mails. Multiple recipients were just doing single email in this situation. But you can see that maybe you have sent e-mail, send email to multiple send e-mail with an attachment, et cetera. So you could define different methods inside the E email sender. You're not confined to just this one that we're doing. All right? So create single e-mail. And then we're going to have to fill this old according to WHO they've stated it in the, in the parameters for the constructor. So the Fromm comes first. And then we say too, we have those. And then the subject, I can see email lot subject here. And the plain text content would be the e-mail body. And you can see that they have the plain text content versus the HTML content. So based on how you encode your e-mail, you could put that e-mail together accordingly, right? But for now I'm just going to say email dot body for both of those parameters. So now that we have the message object formulated, I'm going to say var response is equal to clients send emails. So this is where we're actually going to send off that message that we just created. Of course, we're getting that red line because we need to be a sink. And once that is done, we no need to return a Boolean based on the response. I can just say return if response thought status code is equivalent to and I can just say system.in it's HTTP status code. Okay. And I believe that SendGrid also caters for accepted someone to do both, some returning. I'm just returning. It's either okay or accepted. So that is going to if it's either one of them is true. If it's neither of them, then it's false and they will know if the email was successful or not. But the whole point of this, once again, I'm going back to our create Tumblr and the way that we wrapped it in a try catch, the API that is calling this handler or whatever code is calling this handler should not be interrupted if everything else. So this is the most important part creating the leave requests if the e-mail fields, it doesn't mean that it should crash the program. So that's why we're catching the exception, but we're not doing anything to throw it told are through all the application. Now to top it all off, we're going to have infrastructure services registration file, just like with every other project before it. So we have the infrastructure services registration class being added to that infrastructure projects. And then we have the same form that all the other distribution classes have had. Once again, I configuration is being injected. And the type that we are, the namespace that we need is the Microsoft extensions configuration, not autumn upper and I'll bring it up every time because it has caught he more than one, sorry. So we're going to see services dot configure Email Settings. So this is all studying it that we want Email Settings. I'll just bring that one in to B relative to a configuration server mouseY emails setting when we do the dot options, we're seeing give me a chunk of the configuration that looks like the object EMEA settings. Well, what we're really going to be seeing is configuration dot gets section. And it will look in the app settings for our section with the name. We will call me e-mail settings when the time comes. So then that way it will know that we will formulate that section to look just like what we're expecting the class to look like. So it will just automatically serialized into that hard coded class are strongly typed glass rather. Next stop we're going to see services that add transient. Now remember I was talking about the different models. We have. Singleton, sculpt and transient. So transient means every time I get called, I am going to be a brand new instance. So we're seeing that every time I email sender gets called, Give me a brand new instance of the, of the email sender class. Alright, so go ahead and include any missing references. And then we can close that one off and then we return services. So that's it for us sitting up our infrastructure, at least that's a very basic level. Once again, any contracts that we will have to define for a third party operation will be defined in our application, but implemented in our infrastructure.
19. Create and Configure Application API: Welcome back guys. In this lesson, we will be setting up our API projects. So we have the foundation already in the form of the infrastructure, the persistence projects or domain and our application projects. The thing is that these will evolve with your application. So these layers are these projects. They're not set in stone, but we at least set the foundation to build an API on top of it that will actually be in charge of toxic information between any client applications and these layers for us. So let us get started setting up this API projects. We're going to place it in our folder called API. We go to Add New Project, find it easily, can just search in the list for API. And we're using the C-sharp API project template, and we're calling this one HR dot leave management dot API. Go ahead and hit Next, and we're using dotnet five are the latest version because.net is very backward compatible. So most, if not all of what we're doing this course will be compatible with future versions of dotnet and we will continue with the settings and create. Now this API project is pretty bare bones. It does give us some sample code in the form of this weather forecasts controller, which we won't necessarily need. Before we move forward, let's add the dependencies that this API project will have. And those dependencies include our application project or infrastructure project and or persistence project. Pretty much any one of the projects that we would set up these register services methods in. Those are going to be dependencies for the API because the API needs to be able to register these services in its codebase. So we can just go ahead and add these project references. And once we've done that, we can continue our configuration. So when we talk about creating and configuring we've created no, we need to configure. So in terms also of the things that have dependencies on what we are going to be sitting up in the API. We're talking about the e-mail settings. So remember that in our infrastructure we would have made mention of the email sender or would've implemented email sender other and it is depending on Email Settings coming over through AI options, e-mail settings, which we already discussed is going to be coming from our app settings file. Another thing that we need to set up is for our persistence layer. Sorry, I'm just picking wildly for persistence layer or DB. Context is reliant on a connection string that will be coming over once again from the API. So our app settings file here, we'll have blocks or sections that have those definitions. So let's work on the first one, and that is our connection string. So above this logging definition here, I'm just going to press Enter and then I'm going to put in my connection strings section. So connection strings. And then it has the same name as what we would have referred to in the persistence redistribution, right? Get connection string. And then it's leave management connection strings. So connection string knows to look in the app settings.js ON look for connection strings, get that one by name. And its definition here is I'm using the MS SQL local DB server, so you have to type it just like how you see it on the screen here. That is built into Visual Studio. Database is equal to, and I'm calling mine HR underscore leave management underscore DB. You can call it something else if you wish. And other than that, and each one is semicolon separated and then we have trusted connection is equal to true semicolon multiple active results sets is equal to true. So that is your connection string. Now for email settings, we're going to have a new section called Email Settings, and then we have that API key. So I need to, we need to go with the SendGrid, then gets our API key suddenness, putting a place holder for null. And then we have the from name, that is where the email will seem to come from and then the from address will be no reply at leave management.com or leave reply. No reply, sorry, at AHRQ.com, whatever it is that you want there. Now in case you're not very familiar with what SendGrid is. It is a two-level product that allows us access over Paul for email API system. And you can just start for free. It's free up to a certain point, of course, and you don't want to abuse it. But once you sign up, they'll give you that API key, which is what you can stick right there in the e-mail settings, in the app settings.js IN. We can do that later on. Of course, I don't want you to see my key because the key is private, so you want to make sure that you maintain the privacy with that. So now that we have our AP settings.js, JSON file at least o fitted with the minimum four or distribution of our services. Let's jump over to the startup.js. So they are not some of the startup.js file. It's basically the container that says these are all the dependencies that my application needs to know about. And I am making them accessible through dependency injection. All right. We've been talking about dependency injection for a while now. And each one of these registration files is basically allowing us to register these as dependencies in an application. So what we need to do is let our API know that these are dependencies each should know about. So when we talked about the icon figuration, you notice that it's being passed into the startup while it's being injected right into the startup. And that allows us to then pass that configuration object into other parts of our redistribution. So we would have seen it in the infrastructure if I'm not mistaken, there we go. I configuration needs the configuration and we do need it for the persistence also. So enough talk, let's get into the action. So in our configure services method, we want to include these three lines, which are the Configure Application Services, configure infrastructure services, and configure persistent services, all of which we know boat coming from our or distance services, service redistribution files in the different projects. So here is the catch-all. So it'll just go ahead and add any missing dependencies and namespaces for each of these. Please note also that we have to pass in that configuration objects. All right, so we injected it into the startup and then we can pass it along so that when those methods are being called in their respective projects and they do have the necessary tools to access what they need to access from our app settings. Another thing that we want to set up in this API is, or what we'll call CORS policy. So our course policy basically determines how the API allows other clients to interact with it. So right now this policy is pretty open. Where does seeing services dot-dot-dot cores builder allow any origin, allow any method, allow any header. Then in our configure method, we're going to go down and I'm just going to stick it here between Use Authorization and use endpoints. And this is lightened up nor that it should use that policy throughout. Now we've come a good way with all the configurations. We have one more step left for this lesson, and that is to generate our database. So we've done all of this setup. We have the persistence layer now we actually have it wired up. No, we're actually passing over the connection string. So now it knows that when it exists, which server it should exist on and what the database name should be. So let us get our own to come configuring our database. So we need to run migrations. And I'm just doing a quick build which was successful. So before we move on to the migrations, we need to get access to our EF Core tool. So go into Package Manager, all that. It will search for tools. And sure enough, Microsoft dot Entity Framework Core tools is the second one in the search results. So I'm going to go ahead and install that in my API project. And with that setup, I'm also going to set the API project as my start-up projects. So I can do that from this drop-down, Many up top, or I could right-click it and say set as startup project. Now with all of that done, we can proceed to our Package Manager Console. So if you don't have it in your menu items or as a toolbar like I do, then you can always go to Tools, go to NuGet package manager, and you'll see the console listed there. In this Package Manager console, we're going to add SHE migration and we can give it a name so I can call it initial create initial migration, something to indicate that it was the first one, right? Another thing that you'd want to do is change the default projects. So the default project pretty much needs to be the same project where your DB context is some setting that to be the persistence project. And for contexts, remember that in our persistence project or a DB contexts, we had said that the model in the non-model creating, we said apply configurations from assembly and wherever this is that assembly, That's pretty much what that line was specifying. So we need our migrations directory here. So make sure that that is set. If you don't, you'll get a nasty error. A boat it being set to the wrong target project. So when we go ahead and add that migration is going to do its magic. And then we get this migration file, which is giving us all of these tables that we had created initially. So quick tour of our migration file, if you're not so familiar, we have an op method and add-on method. The other method basically has code and if you just read it as a C-sharp developer, as an SQL developer, you see what it's doing. It's creating a table with name, with these columns, with all those fix on each column. All right? And then the down means that if you undo this migration, these are the things that will do. It will drop the same tables. So for every up there's adult. So now that we have our migration file existing, we can go ahead and update the database. So the database basically says if it doesn't exist, are created. If it exists, then I'll apply the changes. It didn't exist. So it did create it just snow. And to check and verify that it's created, we can go to SQL Server Object Explorer, expand and expand the local DB slash and Mrs Hill local DB does the server we indicated. And if you're using a different server, then you can proceed to that particular server. And when we expand, it's a basis, we will see our EHR DV management dB. And if we expand the tables, we will see our tables that were defined accordingly. We didn't see any dangers. All the tables are empty. Later on when we start building our own application, we can go ahead and look at how ISI data. But for now, this is mission accomplished.
20. Implement Thin API Controllers: In this lesson, we'll be looking at adding mediator services to our API. Now, the context for this is that media to allows us to specify behaviors in our application based on a response on handler type of relationship. One of the consequences or benefits from this is that we can ship old a lot of the heavy operations from our calling code, which in this case is going to be our controller. And we can abstract that to somewhere else. So the controller really just knows I build a request and I send it off to be handled. So for context, I have on screen the controller from the previous project, which is inspiration for us redoing this need management system. And here you'd see that we don't have thin controllers, we have fat controllers. So they're controller, really. Those exactly what the name suggests, controls the flow and everything that the application does. So it responds to a user's requests for data, et cetera. Now, in the older project, we had what we call fat controllers, or we're doing everything inside the controller. Once again, this works. So it's not that it won't work. If it's done that we will work. But is it the best practiced Holman Tenable is this? Because if I wanted to verify that these operations are being done successfully, I would have a lot of difficulty unit testing it in this fashion because this is not in a union, this is enough inside of our controller. So that increases my inability to test the parts of the application to make sure that they are functioning properly. And it increases my time spent troubleshooting and regression testing and all of those things. Here it's checking if something exists and if it doesn't exist, it's returning not phone. And then it's doing mapping and it's doing a find, and then it's returning the view. Read Andrew, just want as few colors as possible with no business logic. This is some form of business logic. We don't really want that in our application. All right, here's another situation where we're creating our record and we're just doing too much in this option. So those are the things that we want to reduce when we talk about with having thin controllers. So let us get started by installing mediator in our API projects. You know the drill, you can just right-click go to NuGet. We searched for mediator and go ahead and install it. Once you have completed those steps. Once you're done with that, let's go ahead and add a new controller to our project. It's an MVC controller and we want an API controller with redirect options. We're going to start with the easiest one, which is leave types and go ahead and add. Now let's explore how media to will kind of help us along, right? So we already have the basic good options being generated for us thankfully. But then what we need to do is inject our mediator object into our controller. So you're going to work on stroke. And then we're going to make reference to I. Mediator will require us to have that using statement as well as to initialize that field. And then we can start using this media to object to Arcbest streets or cause snow. Quick tour of this controller. And at this point, of course, I'm assuming that you have some familiarity with controllers. And by extension, API development, when we want to get the list of records are all leave types in the database are going to hit this method which is just the gets its API slash leave types. That's what's expected to get to us, but all the leaf types. And then each of these methods would carry over to the other crud operations that are pulls the put, the delete. It is for updates. Or if you're not so familiar with what I'm talking about, I would encourage you to go and check off my API development courses so you can get up to speed on fully appreciate what the intricacies are behind these methods and how they work. So let us continue for the get's that is expected to return all the leaf types in the database. We're going to have code that looks similar to this. We're going to have var leave types is equal to an await. So the method send found in our media to object and all what exactly are we sending? If you look at the overload, it is expecting an object of type or an object with the apartments are called requests, so it doesn't know what kind of request it's going to send the request. And the expectation is that it's going to get something that can be stored inside of leaf types. Know what requests would we be sending? While jumping back to our application project features, leave types and queries. We will see here that we have the get leaf type list request, which is expected to return the list of leaf type detail. So in our leaf types controller, I would say I am sending a new object of type list request. And then go ahead and include any missings using statements. And then I will go ahead and change the method header to public async task action results, list leafed out details. So now it knows that it's expected to return an action results that has that list of leaf types. So this returns statement is no longer valid since no, I will be returning leaf types and look at that two lines. Contexts. I'm just going to compare it with the old code or it's our old our old option would have had the variety of types. It would've actually orchestrated the call to the units of work to get the records, bring them directly, buck. And then we'll be carrying out the mapping because it would be returning the domain objects when we would want the VMs are contexts so that it's the same thing as a DTO. And then we would be doing all of this operation inside of the action itself before returning the view. In the new paradigm, what we're doing is we're just letting media to know that we are requesting the leaf type list or handler is going to carry out all of the operations, is gluing all of the mapping and the querying and everything, and it's returning just the objects that we need to know about. So are the API would never ever interact with the actual domain objects coming from the database. We do all of that transformation before it comes all the way back. So let's fast forward a bit to where I've already written the code, but as usual, I'll go through it slowly and explain each line so you can fully appreciate what is happening. You can pause as needed and replicate. And we will go through together anyhow. All right, so a one quick adjustment to the HTTP GET I've limited return on, okay, with leaf types. All right? And you notice once again, task, action results list the five dB HL. So this is being very explicit as to what data type it is going to return null for the HTTP GET with an ID. Once I've done similar, very similar code task, action results leave diabetes. Of course it has to be async. And we say var leaf type is equal to and then we await the mediator Dotson with the new request. So remember that the request will get handled. Mediator is taking care of that part. We have to make sure that we use the correct request based on what we want. So in this situation, the request is to get leaf type bt EverQuest, which is where it's getting the particular leaf type with all of the intrudes and any other fund dangles or requirements that might be there. For the details. We also need to give it the id value that you will be getting because in our handler, it will rely heavily on the ID to know which record needs to be retrieved. And then we return, okay, with the leaf type. Now in the post, we have a few more lines and it's really just me showing you how you can break it out anyways, but it's not absolutely necessary. Because just the same way that everything could have gone in 19 and we just create the new object right here. I could have done that inside of this one, media to send line. Instead. However, I said var command is equal to a new create leaf type commands. So the post method is designed for creation. All right, so let me start from Desire2Learn BAC chart, but I had of myself there public async task action results. In reality, you don't necessarily have to see what the return type is. It does help with the documentation on the ghetto to swagger. So I would put it back. Regardless in this situation, the response would be an int. So we could see int, but like I said, it's not absolutely necessary. So I'm going to continue without it being necessary right now and later on we will see why it would have been a good idea to put it in. So task, action, result, post, and we're doing from body, and then we're using the specific detail type for the operation. So remember we discussed the you get granular. Leave it general. Well, this is a situation where we would prevent over posting by getting granular because then when they spin, when they send over leaf type information, what we accept through this option is limited to the properties inside of the type that we specified. So unlike leaf type detail or leaf type BTO, which has more details are more fields they create. Leaf type is only designed to accept the data points that we know we absolutely need for our leaf type to be created, so anything else will be ignored. So when we formulate this command, we said new create leaf Command, D, command. Needs leave that detail object. So we pass in that object. And then our response is relative to what the mediator returns with. In this situation, our response was int, because that's what we had said. When you create the leaf type, just return its ID. So that was the handler that we designed for that kind of requests. So then when we return all k with the response, that would be okay with the leaf ID. Now moving on to the put, put is used for the update, once again is in task action results. And then it's by default have that ID parameter and we change from body perimeter to be the leaf type DTO. So we did, we had discussed from earlier that the leaf type BTO, we didn't get too granular with that. I have an update div tag, div TO different from everything else. That's fine. So in this situation, we're accepting all the possible fears are could be updated. And we definitely need the ID inside of this object, which is why we're attaining that. As a matter of fact, does it stuns this ID is optional. So I could actually just say, I don't need an ID parameter. So when you call the put, you don't have to call in an ID. And then this comment would actually just update the comments establish uniformity. So then task action result, put an hour looking for is the body contexts with the leaf type DTO. Remember, all of our validation is happening inside of our handlers. So that's even less. Fewer things tore bullets right here. When we're talking about did the ID comb-over, does the ID exists? All of those things are happening inside of our handler, between the handler and are fluent validations actually. And if anything feels on that side, then this whole operation will fail. Anyway. We'll look at how we handle failures later on. But for now we just wanted to get a handle on how our controllers need to look. So when we send the command to update, we did not wired the soap to return anything, at least nothing useful, right? We just said Unit dot value unit represented a void, so we had to return something, but we just said, okay, you're just going to return something arbitrary to say it was successful. And so the HTTP response that corresponds with a put is usually no contents, which is a tool for, alright, so we can just return that we don't talk to us and the response to any variable. Delete looks similar. Delete takes an ID parameter, task, action results, delete int id. And then we have the command which just takes the ID, and then we send over the command and return or all content. So if it is that you want everything to be just two lines, then that's as easy as taking the command, putting it in the same parameter. And everything can be two lines. All right, so I'm just showing you the difference, the big difference between this controller that is doing everything that this controller is doing with all of the code, all right, with the edits were doing validations here. For the Delete, we're doing some form of validation. Again, all of those things are null, obstructed old into other parts of the application. And our controller can do exactly what it's supposed to do, which is receive a request, make a call to do some operation, and then give you back your data. Now here's my challenge to you. Go ahead and wire up the other controllers for the other types, for the features. So we have leaf types are done. Go ahead and try the requests and leave allocations. You can, of course, create custom options relative to what it is that the operation needs to carry out. But for now, I'll leave you to do that. So I'm going to come back in the next video. We'll compare notes.
21. Finishing up Thin API Controllers: All right, So this is more of a review video then a listen video. Only have a few things that I want to focus on in this lesson that you probably attempted and probably had programs with an if not then kudos to you. So let's start off with the leave allocations controller, really and truly, this is a control that is going to be pretty identical to our leave types control because really and truly we are only doing crud operations here. We're getting the list where getting by ID. And of course if you didn't complete it, you can always just pause and go ahead and replicate as you see me going through. We do the same thing for the post. So you'll notice that everything that pretty much said leave requests is noticing leave allocation. You could almost say that you could have created a new controller, copied everything from the leaf types controller, pasted in the new controller and then just policed the leaf type with leave allocation or type with the word allocation. I'm just giving you tips us the whole you could have done this pretty quickly and pretty effectively, right? Because this one is pretty identical to the leaf types. Leave requests, on the other hand, has one tiny surprise. And that is in the form of the update, right? So once again, going through slowly enough, I pretty much are replicated the options from the leaf types over in leave requests. So most of the content of this controller is identical to the other two, with the exception of our PUT operation. And if you notice and look very closely, I would say put operations x2. So everybody else only at one put operation one, update endpoints. But then in the case of the leave request, we had made room for two types of updates. One where it's a regular updates. And notice that this time we do have the ID parameter in the put. And that is because our leave requests command asks for the ID and for a detail, that's fine. But once again, these different flavors are relative to whichever style you think. Much easier scenario. So I'm just showing you different options. I'm not saying this is how it must be. You have the different options. Use the one that is best for your situation and your project. Now in this situation, once again, we do have the ID parameter we possible with the command and we build a leave request DTO. However, the other updates scenario would have only a change approval status for our leave requests. So in that case, I created a custom endpoint change approval. So to get to this one, you say api slash Requests Controller slush approval. And I really do need the ID. So let me go ahead and add it to the root so it will be changed approval of Slashdot ID value. As I've also updated the documentation accordingly. And I need to put that ID parameter, buck. So tricky told earlier, put it back and build up my request objects, all right, or my command object rather. So I'm just showing you all of the considerations that need to be made. What at the end of the day, our controllers are slim and the benefits of this you may be seeing, okay, so three lines, but all of that work or in the earlier videos, just so I can put three lines here. Why didn't Titus put all the logic here? Which ones again, I completely understand because it would work. But at the end of the day, quote, is far more testable and I don't have to test the controller to know if I would get the results from this end point. Instead, I can go and test the handler that is supposed to be returning those results. And if the handler works, then the endpoint will work. So, you know, it's just shifting your focus from being more compact to being a bit more modular and spreading the code and the responsibility across more pleases so that you can have a better appreciation or a bitter control over what each component does unhedged, they all tied together.
22. Seed Data In Tables: All right, so we're winding down with our API related operations. And in this lesson, what we want to do is seed some default data into our database. The next step, of course, would be to test it, but then it's always good to have some sample data so that we can do the read operations quite easily and effectively. So to see data into our database, we're going to look at how we accomplish that with Entity Framework. Now I've done one already and I have the other two ones done by a, so we can do them together. But let us start off by going to the persistence project, add a new folder called configurations, and in there another folder called entities. And then you're going to have a configuration file per entity. So pretty much this configuration file allows you to put in any Entity Framework related or any database related rather, configurations are defaults or any rules that you want to go over in the particular table in the database will then all of the code is written courtesy of EF Core. So let's look at the one that I have done already, and that is for the leave type configuration. So you can pause, take this off, and then we can go through the bits and pieces together. So we have the leaf type configuration and then it's inheriting from I entity type configuration relative to the class type that we're dealing with, which is the leaf type. So then everything in this basically codebase is going to be relative to the leaf type. So once you do that, you're going to end up with, it's going to ask you to in parliament the interface and then that would generate this method stub for you. So inside of this method stub, we're going to have public void configure. And then we have that int. It's all of that is actually generated for you. So the more the main parts of it, which is what you will be building or writing in, would be in this Builder section. So we would say builder has data. And then we would say leave type or creates a new object. So this is a method has death is a method that has open and close braces. And then in there we see new leaf type and then we fill an object. So this is the domain object that we're seeing your IDs one, your default is to go into databases 10 and your name is vacation. And then as many as you need to, you can actually just coma separate each initialization of an object or instantiation of an object inside of this entire block. No, I haven't done the other two. So I'm going to do them kinda from scratch. Even though by no, you probably did it already with the leaf type. Well, that's fine. So for leave allocation configuration, it's inheriting, but there are no using statements at all. So that's why you're seeing the red line. So I will use control dot using Microsoft Entity Framework Core, the domain reference, and then implement the interface which generates that method stub for me. But there is nothing there for me to configure for the allocation. I don't have a requirement for the allocation at the moment. All right. So I'm not seeing any leave allocation. I'm not changing anything about the defaults on the table structure. I'm not doing anything else. So like I said, this configuration can be used for far more than just seeding data. And if you want a better understanding of what it is capable of, you can always check all my Entity Framework Core course. All right, so we'll do the same thing for the request. And we'll just leave that there once again, we don't have any leave requests that we need as defaults, but we do have some default leaf types, so that's good enough for now. At least we can run the API and do GET requests on this table and verify that it's working. So the next step after writing this code would be to go to our Package Manager console and we need to add migration for seeding leave types. So once you do that, we get to a migration file that is letting us know that it will be inserting that data into the database for us. Next step, as we know, would be to update database. Okay, great. So this time we're not going to go to the database directly to verify that these were created. What we will do is test our API. So see you in the next lesson.
23. Review Swagger API Support: All right guys, welcome back. So in this lesson we're going to be looking at one, testing our API and to documenting it. So the key pool that encompasses both of those tasks is called Swagger. Swagger is an open source API documentation tool based on the open API standards. Now it comes out of the box for dotnet five API projects. So if we jump over to start up and scroll a little whose see here that we are adding the swagger Ginn Library, which creates that Swagger doc with an open API info. So we can change all of these things. I can just see HR leave management API. Give it a title, give you a version. You can add other nodes to it, contact description, et cetera, et cetera. So it's a very powerful tool. And like I said, it's comes out of the box and you didn't put that there. And another part that you would look at is down here in the configure method where it says, if we're in development, then use swagger and API. So if you want to use swagger, because I know of corporations that was used soccer as their documentation in production, then you can always use this salt of that if statement and actually use it regardless of your environment. All right, so you can go ahead and make that change. No, let us run our projects. So I'm just going to hit F5 with the API project as a startup project. And that results in us getting this beautiful document showing us all of our potential endpoints and how they can be called it. Notice we still have the weather forecast. We can delete that afterwards, but I'm just showing you that we didn't do much. All we did was set up our controllers, write the code that we know we need to write. But here's this beautiful document showing us everything about our API. So if I click on one of these, it extends and it shows me exactly what I can expect. So this endpoint, which is R, this behavior api slash leave allocations, which is the get that gets all of the records in the leave allocations table. It's going to give me a 200 success code. And this is a preview of the object that would be getting buck. So once again, I'm going to always go back to details and how granular you get on what you want to display. Notice that in this particular detail, we have the ID, we have the number of days, we have the leaf type with its own ID, the name, and the default is, then we have the leaf type ID. So you might be looking at this and say, well that's kind of redundant. If I already have the leaf type object, I don't want tough to repeat the ID here. And that would be a fear statement, right? So, you know, we didn't get very granular with the leave allocation list DTO. In this situation, we just use the new allocation detail. So we might be sending too many details are too many fields in that response. We can adjust that accordingly. So let's look at leave requests. Leave requests would have ID, the leaf type, the date requested and approved is true. Why does it have this as opposed to the one that is getting the details which has much more. So swagger is actually looking at our return types on the particular control as our options rather. And I'll just jump back over to the controller so you can see what I mean. Remember that I will say in that there is a benefit of putting the return type directly in that I actually result because saga is actually using this to say, okay, this is the data type that will be returned from this auction versus this datatype for that action. Or outside of that, it will just make an assumption. And the, the return type or the fema that you might see might not be representative of what's actually being returned. So that's one of the benefits that are remote said there are benefits to putting the return type here. That's one of them. Saga will infer the objects that it needs to show the schema for in JSON. And that is least a better and clearer documentation for those who will be interacting with your API. Alright, so let us run a test. Let's jump over to api slash leaf types. Since those have been seeded into the database, we should, I expect at least two records after we tried told. So let's hit that end point to try it out and execute on. What I'll do is set a breakpoint on the controller and the handler that this operation should hit. So when I execute to 2Ts, the control or the option rather, right? Remember that's it's going to mediate a Datsun and symbol with that requests no, I'm pressing F5 and then it's going to hit the next breakpoint, which is the handler. So you see when from controller to handler and where the all of the magic is really happening inside of the handler and the handler, we have our repository under mapper. Both initialized injected. And then in the handler, we carry out the query, and then we return the detail version off the data. All right, so I'm just going to hit F5 again and allow it to complete its operation. And then swagger then shows us the data coming back from the database. There we go, named vacation default is ID1, et cetera, et cetera, et cetera. And in the same way, if I go to the get and I tried to tote, then it allows me to pass in the ID. So I'm going to pass in ID1, execute, and then it brings me The Wanderer card with the ID one. Now soccer allows us to do all of the crud operations, at least test ever endpoint that we have laid out. So let us try another one where we're going to create. So notice the difference between this schema and the schema being returned by the detail via gets, right. This one has the ID, the name, the default is this one only has name on default days. That is because of course, we're using a different detail with limited scope. So for the name, I'm not going to change anything here. I'm actually just going to put, leave this default data. Let's execute and then look at what we get. We get a 500 error. All right, why did we get to 500 error? Let's read it closely. So remember what we'll put it. We didn't change anything. The name would've been string and the default, these would have been 0. If you remember carefully, we had setup validation to see that the default is should never be less than one. So what we're seeing here is a 500 error because the API, I didn't know how to handle the fact that it's getting an exception all the way from the handler. And this exception is of type validation exception. Does that look familiar? Right? So it is just letting us know that there was an error and this exception was thrown. We don't have any exception handling built in. So everything that is being thrown, swagger doesn't know what to do is just showing us what the application gave you, which is fine. That's what it's designed to do at the moment. And we will be refining that as we go along. But the point is that this is working. So if I put in something more meaningful and this time I'll put in maternity leave with default days of 90 execute again, then we get back our response code of 33 would be that ID. So I can go back to the get testlet 3 is execute. There we go. Maternity leave default is 90, et cetera. So I'm just showing you how useful swagger is for you to one see the document for the API and to test without having to install any other application. Now that being said, I generally use Postman to do my API testing. But for the purpose of this course, Swagger is perfect and it has served its purpose.
24. Unit testing - Section Overview: We know will have a large portal for application built. We have fleshed out most of what needs to be there for the foundation. We have set up the API and we have tested it to some extent where we see that it's actually communicating with the database and it's working art. But then, as we have seen and have discussed more than once, testing is a very important part of application development. So we just erupt up testing the API Andrea to do that manually, where we are to actually put it into the testing tool for the API submitted. And then we see that it got submitted and all. And we were then confident that our code is working. But then imagine a much larger obligation with many more touch points. We only tested one touch point, just know, but then imagine testing 50, 60 endpoints. You don't have the time or capacity for that. It would be a waste of time to really try and go through all of that. That is why we are going to be looking at unit testing and how it can help us to automate those checks to make sure that our code is doing what we have designed it to do. Now in a nutshell, unit testing is code that tests code. Yes, that's right. We're going to be writing code to test our code. This is one of the reasons people shy away a proton because some people see it as a waste of time because then you'd be writing code twice. And that some people see does completely essential because they don't trust code that has not been tested by a unit test. There are persons who subscribe to either extreme. I'm not necessarily one of them. I believe that tools are used within the context they're designed for and at the time that you're ready for them. So in this situation, it's definitely a tool that we want because we want to make sure that our handlers are behaving consistent with what we have designed them to do. We also want to write what we'll call the integration tests, which will just the interruption between the different layers of our application. Unit testing. Once again, it works so really great in saving time. In the long run. It takes a while to write the tests. And our test is only as good as how it's written. But then there are frameworks out there that can help us to ensure that we have quality tests and that we are having full coverage for our quote, another thing that unit testing helps us with is documentation. So he could leave documentations and comments all over your code. But well-written tests can actually show you our indicate to you what bits of code should be doing here and there. So some people actually use unit testing as a way to document their code in an unofficial and manner. Now in general, there are three main types of tests that we usually cater for. One is unit testing, the other one is integration testing, which ones again, tests the interaction between the layers. And then functional tests which are usually like UI facing to test what the user experience should be. So when we come back, we're going to look at writing our first unit test and we'll be setting up our test project inside of artists folder. And we'll be testing our application logic, aka the handlers, to make sure that they are doing what we think they're doing.
25. Write Unit Tests for Application Code: Hi guys. So let's get started by creating a new test project for our application layer. So in our test folder, in our solution, we are going to create one I called mine HR management application that unit tests. So from start, it would be new project and we're looking for an x unit project template. And then we want to give it the name of course, and it needs to be done at five. Alright, so once you've done all of that, then you can jump over to NuGet and we want to install MOQ or mock and shrewdly. So Mach is going to help us to create mocks of our persistence. There are repositories and other objects that are needed to simulate what our application or our handlers are really doing. And shouldn't is helping us to assert that this is what we expect from this kind of operation. Now I've already gone ahead and set up a folder structure for you. And I think they're more interested in things for stackable than for you to sit down and watch me type. So I've gone ahead and prepared some of the assets beforehand, but as usual, we'll go through them slowly and together so that we can completely understand what is happening on each, at each step. In this project, we have two folders at least for no, So we are 14 leaf types. One for Mach smokes will be hosing the mock repositories. Now you have two options. Of course you can have a file per mock repository, can have one mock repository file and have multiple instances in there. That's up to you right now I'm only using one file and what will only have one test to run. So if you have 50 things are features that you need to run. Our handlers are features that you need to run unit tests against, then it would probably be better to just split them out into particular files per datasets that you would want to have a mockery before. So let us go into the mocks first. So I've already kind of gone through and setup a method in this, in this file. So let's discuss it together. So I made this a public static class and I'm calling a mock repositories. Once again, I could easily have been very specific and say something like mach leave type repository. As a matter of fact, I'm going to take my own submission and just do it this way. So we have this file, particularly for Mach leave type repository, the Toronto Newcomb buck well, mock lever location repository, et cetera, et cetera. So Mach leave type repository is a static class and it has a static method in it. This method is designed once again public static. And notice that it is decorated with this keyword mock, which comes to us courtesy of the mock library, right? And it will allow us to mock any type of repository here. So I want a mock leaf type repository mocking the elif type repository. And then I'm calling the method get leaf type repository. Now this method is going to have a list of leaf type objects. You can be completely fictional with these. There's nothing to say that they must look like what would be in the database. There's no, there's nothing particular about it is just a list of objects similar to what we would expect from the database when dealing with the domain leave type objects, right? So I just have to, you could have ten, you could have 15 or 20 would have more based on your scenario and what you need to test for. You may need more, you may need fewer than that, but that's up to you. So I'm only proceeding with two. All right, then we initialize our mock repo. So I'm going to say environmental group was equal to new Mock. And the type ones again is I'll leave type repository. So until we do all of this, you're going to be seeing our red line beside this method because it's saying that not all paths return a value because we need to return the mock. So before we can return the Mach, we need to set it up. So sample data is present. The new object of the Mott repository is present. So now we need to literally call mock repo dot setup. We use a lambda expression where it gives us access the methods that would have been inside the original repo. So if we want to test the get method or at least set up, they get all methodic means that when a test is calling this mock repo and we want to invoke a test against code that is using this gets all. It will pass in the mock repo. And then we're sitting up, they get all method. So this is one block or it's a setup. Our lambda expression R-dot ghettos L, That's all in one block, and then returns the list of leaf types. So any code that gets the mockery born is going to call the Get off for this repository, the mock. Which will be invoked in the test, will return that list of leaf types. That's what it will deal with. All right, so you can, you're in complete control of your sample data. Tend to T does to get up and started with this Misha with the sample data, but it's all for a good cause. No, the next one would be to set up what happens when we call the add method. Alright, so mockery blue dots setup, once again that lambda block where we call the add method. So all of that is a block. And I'm going to just explain what's happening here. We're seeing our dot-dot-dot normal, but the add method by default needs an object of type leaf type entity. All right, so I'll just leave it on. All right, so we have to pass a leaf type entity into the add method. What we're doing here is that we're kind of doing an assertion that you can only call this method when an object of type, leaf type is being passed in. Once again, this can be a dummy object. It just needs to be of type, leaf type. It doesn't have to have all this data and anything special. All right, so if it is off the leaf type, then this method can be called. And it returns is sink an object. So what we're going to do is do a delegate to say after getting the object of type leaf type, and then we have our Lambda expression or method block them, just move it to the next sentence so you can see it more clearly. Then we say leave types, which is our list dot add. So for the lifetime of the test, whenever somebody calls the add method and passes in that leaf type objects, we're just going to add it to the list of leaf types and in return that particular leaf type. So that's just hold the setup works and then based on the scenario or testing Fourier setup may differ. So you notice that they get all setup looks fairly different from the, the, sorry, they'll add setup. And then the delete one mil differently and then the update one mil differently. So right now we're just sitting or for add and get all. And then we go ahead and return that mock repo. So after returning it, that arrow would go away, and that is what this method is four. So after you've replicated all of that, you can just, I'm sure you are pausing and writing it don't, but I hope you have a better understanding of how the MAC helps us to simulate data without actually touching the database. Now let's jump over to our first unit test. Now this one is going to be queries, folders. I've leave tabs and commands and queries. All right, so the handlers that are doing queries will be tested inside of queries will look and commands in the commands. So the first one that we have is the get leaf type list request handler test. It's almost full, but at least nobody can make a mistake as to what is being tested. Instead of this file, you can have multiple tests of course, but then in this scenario we only want to have, we only need one test really for the guest list handler or no, let me just walk you through what is happening within these first 10 lines. Well, we need we know that we need auto mapper and we know that we need a repository in order to interact with the handler, right? And I'm just going to jump, don't I haven't finished writing the tests that we're going to do that together, but I wanted to just demonstrate what happens when we try to instantiate our handler, we're getting an error. Why? Because a 100 is expecting a parameter of type I elif type repository and another parameter of type high mapper. Now this is a unit test who can't just inject them in. That's 12. We wouldn't want to inject it in. I mean, we probably could, but we wouldn't want to because we're dealing with mocks, right? Don't want to inject in the real elif type repository because that's going to talk to the database. We don't want our unit test to actually talk to the database. We just wanted to simulate it. So what I've done is initialize or declared or other two private fields, one for the mapper and one for our mock repal, right? So private read-only mach of Type II elif type repository people. Then we have our constructor. So in the constructor, once this test is invoked to on to initialize our local mockery boat to be equal to call the mock leaf type repository class dot get leaf type repository method. Alright, so that no, we'll do all of that setup and return the mockery people for use in this test. Now for the mapper, we once again, we're not injecting anything. So we need this mapper to know of all the actual mapping configurations that exist in our application. So what we're going to do is initialize mapper config object. Be a new market mapper configuration. And then we have a lambda expression here with our object block within which we see lambda Tolkien dot add profile for the mapping profile. After we've set up the mapping profile and the configuration, then we can know just pass that didn't. So we say mapper is equal to mapper. Config dot creates a new mapper using that configuration. So in all, our mapper here in artist really is It's representative of the real mapper in the application. So all method below know has public async task get leave type list. So we're specific, know what exactly are we testing for? Where testing for the method that gets the leaf types list. It is decorated with this fact attribute because this is what's telling the obligation that this is a unit test. So this is a saying, Hey, whatever happens here needs to be a fought. Whatever assertions here need to be passed. If they don't pass, then this test has failed. Now let us actually get interact in the hundreds, so are the tests rather. So we say var handler is equal to new. And then I'm instantiating the 100 that I wished the test in this method. Notice the red lines because it requires our mock ripple or some instance of an elif type repository. So mockery Bull gives us a mock object, but then I can get the actual quote unquote actual object through MLK repo dot object. Next stop, we need our mapper. Alright, that looks good. Next up we want to actually test the method call. So I'm going to say var result is equal to Handler dot handle. All right, because there were handler has that method called handle and it requires a new object or it requires the request objects. So we need to pass in a new object of the goal. And I'm just going to take all of this off and uses the insert, the using statement. There we go. So that we don't have a whole of that text. And then after that, it also needs of cancellation. Tolkien, alright, so against leash on can or I can just pass in cancellation Tolkien dotted line. All right, so this bus that on in, yet this spilling red and include any missing references. There we go. So we're just seeing call this. We're not passing any cancellation token in. We're bossy and that new object, so we don't necessarily have to be. So Mach is really just for that, the top. Alright? No, we can see a result. And if you look at the datatype for results, oh, well, we need to await this. Apologies. If you look at the datatype for result, it is actually list. Yeah, there we go. So the data type for the result is a list of leaf type DTO. So it's easy enough to doors statements and say, Okay, well, if the cones is greater than one, then this or something, just to make sure that we got back more than one because the list, so we should get back one or more. But then at the end of the day, we can sit down and think of all sorts of scenarios and so on that we may get overwhelmed. So that's why we have the framework should lead to help us. So I can say result dotplot. Remember that this is really just listed things. It's just a list. But went to type should be off type because I want to make sure that I'm getting the correct data type. The data type that we expect from our handler for this particular call would be the list. The data type would be list of leaf type detail. So I'm going to see and know Mandarin lines for now. Let's just work through this part. Leave type DTO. Alright? No, when I do that, there is pure Larry line because should be of type, you've probably never seen that before. So if I control WC using shortly, so that is where it shouldn't be comes into play. It helps us with our assertions, with our assumptions about what should happen. I'll go ahead and put in the using statement for the DTU also. But then that is the point I'll surely. So I invoked the handler, I run a command or run an operation, got a result. And then I'm seeing that the results should be of type, leaf type DTO. So that's one assertion there. And let's see what does we have Kinsey result dot and the starting, start typing Should I will see all of the possible, right, all of these. Our possible things that we can assert or it should be no, should be in range, should be in order. All of these things are things you can check for. So I can see should be and say T2 because I know for my test data that the shoot, oh sorry. More C dot count should be. There we go. All right. So this should be is just tucked on to any other property or the object itself. And it allows it to get the assertion for whatever scenario expecting. So know that we've said that one. Let me write to the project and say run tests. And that will give us autistic Spore, which will then run the test and show us what past. All right. So that one past four, if it should be two. No, I know I put two, but let's say I said five and rerun this test. Nowhere getting failures. And if I expand that just a bit, who sees should leave assertion should be five, boat was two. So that means if something changed in my code and it mass search on, I know my code is supposed to return to at all times, but then something genes. So let's say I didn't change the test. I know it's too. Right. But then if I put in an mother item. All right, so this is where modifying the code comes in on. And yes, this is all statistic, but once again, we're just talking about sonars. So maternity or if I change the code that should have returned to one, should always return to 18, introduce something. No, it's returning three. My unit tests just by running it will know tell me when something change. So let me just rerun that unit test. And my assertion is still two, but then three game bucks. So right off the bat, I know that there's some bug in the handler that I am dealing with or because something went wrong. So that is kind of what unit testing brings to the table. Now let's create our creates leave type command handler test are. And so instead of commands folder, you can go ahead and set that file up. And it's going to look fairly similar to the get leaf type. We have the same Mach repository being used. We have the same initialization procedure being used. And then we have test, which is the current that of course with the fact and the task where we see create leaf type. So we invoke the handler, which is the create leaf type command handler, singing It's the objects as necessary, and then we have the results. So our result is going to come from our eating handler dot handle create leaf type command. And remember that our leaf type command to create requires a lifetime BTO. So that means we need some object here to pass in null, you have two options. You can create the new object criteria in this handler and use it. But then if you have multiple tests, you can just create an object of leaf type D2 and use that for multiple tests. So I'm going to use the second approach where I'm going to have my private read-only leave that DTO and then I'll initialize it in the constructor. Some people after awhile, they have so many assets being initialized that this constructor grows too large. What they would do is maybe farm ETL to another method that they call like initial, initial setup or initialize. And then they do all these initializations in there. And sometimes assets are actually shared across tests. So what they would do is farm it all to an entirely different file, like a crass dot setup, right Leave type test setup. That will just go ahead and return all of the objects needed. So there are no both ways, but these aren't big tests. So we're just wanted to get the concept under our fingers before we think about obstructing it too much, right? So we have all of that setup and we have our leaf type detail that needs to be passed in. So oh sorry, I'm getting this area because it's the create leaf type BTO, apologies. So let me just fix that datatype and then we should be good to go. And there we go. All right, and of course no ID is in that one. All right, so let us see what we need to assert in this one. For one, when we add a new leaf type tool, our mock repo, then the cone should increase, right? Just the same way when we added to our database, if there were ten, know there are 11. So remember in our mock repo, we had set up the add method that should take any typeof leave type and it should add it to the list. Which means that if I was to call the list or to get all from. The repo right after an add operation, then there should be one more file, then there are null, one more record and there are at the time, right? So there are three null. Let me first the update this assertion on to say three. And then I will call the mock repo. So I'm going to say var leave types. I went to query the repo for Khomeini. So of course we're doing the mock repo dot objects thought, and then we can just get the gets all. So when we invoke yet, all we know exactly what we're getting. We're getting that list. Then I can do my results thought should be. And we know that we are returning the ID values. So after calling the create Tumblr for leaf type, it should be of type int, right, so that's our first assertion. And then I'm going to also say that leaf types dot, dot should be. And I'm going to make sure that it says four because we know we're starting off with three null after the call, it should be for. Now before I run the test, I'm just going to rename it to look a bit more useful, right? So create, create leaf type tests. But what then you can have multiple tests are humans create leaf types of testing for what happens when it's invalid testing for what happens when it's valid testing for what happens when values are particular value and you have a business rule in their work should behave this way or that way based on the nature of whatever is being passed in there. So many scenarios, I can't sit down and think of every scenario, but I'm just giving you the framework upon which you can formulate your tests. So this test is, is an assertion that whenever a valid leaf type is added, This is the expected behavior. So I'm going to jump back over to our handler does so we can understand, again the purpose of unit testing. So testing that we get Buck required, That's easy. Okay, yes, we have the MLK repo where only query in the mock repo, the handler is supposed to do one thing anyway which is returned what's in the repo, that's fine. But then in a situation where we have multiple outcomes in our handler, we want to test for each outcome. And then you can probably break it down into the good test and the blood test. That's at your discretion on what you want to make sure that you have coverage for the potential outcomes. Because if business rules change, if a developer comes in, changes the if statement, even if accidentally, then one of the outcomes will change and you want to catch that as early as possible. So in our create leaf type command, and remember that we're doing some validation here, right? So we validate the leaf type and then we throw an exception. When it is not valid. Then if it is valid, we go through and we return the ID. So we've tested that's already. And we see where adding the thing actually brings back more records. If we were to Git repo. And that's good, but we need to test for what happens when bad data goes into make sure that it is handled correctly according to our business rules. Alright, so that's where we made the assertions. We assert that if I get an invalid request object with an invalid leaf type D DO leave type request DTO leaf ab initio, sorry, that we should get an exception or we should get certain things in their response based on how you wrote your code. This is what should happen. This scenario is MIT. Notice also that because we're at, in unit two, CO start seeing how many references on how many tests are passing for your code. So you can always know that you're in the green or you're in the amber or green or you're not in the green when it comes to the tests passing. So I'm going to jump back over to our test and I have added another test for the invalid leaf type added. Alright, so we have the same code where we set up our handler, right? And then once again that is repeating. So what some people do and what I tend to do at times is move that repeating code up into being a private field and initialized in the constructor. As the testing coal file grows, you may want to farm some parts of it because we don't want to have to do this every single time you have a test, you have to initialize. So it's the same command handler that we're going to be using with a C mock repo and the same mapper. So after sitting on the mock repo, I'm the mapper. We can just initialize our handler to get those objects. So then artist looks, well, we have one less line to worry about, right? So we just say underscore handler, datetime bill, and move ahead. So in this new test, I am going to take my leaf type DTO and I'm going to make it invalid. So remember that we initialized it to have 15 days and that name. But then we know that while we're testing for it, if I'd leave type ever came over with this, then it should be invalid, right? So we're just going to make it invalid. And then I'm going to do something a little differently. No, So we're testing for an exception. Alright, so I'm going to say validation exception e x is equal to 0, it should. So we get a static class calls should not throw a sink validation exception. And then we open parenthesis and you can open and close. And then instead of that you say async delegates Lambda arrow. Now we're going to our weights, the handler call passing in the details. So it literally just this nine we're putting inside of that should throw async method. Then at the end of it we can test to get all the leaf types. So we're going to go into the leaves us, remember we did that up here and we said that it should know before. So after an invalid operation It should still be three, right? So that's what we're distinct area. We're making sure that this did not make it into our mock repo dataset. And then I can also make sure that the exception should not be null. So this is my way of making sure that an exception was indeed throne. So now that I've done all of that, and when you're testing some days you'll get the fetus. So when you get filled as it's either because you need to refine the test to make sure that you're testing the right thing or you're, you've read the test and you're just interacting and it's failing because your code is not passing the test. All right, So two parts look out for. So now that I've done all of that, let's go ahead and run a new set of tests. And I've been getting all green lights everywhere. So the invalid leaf type added all the assertions passed, which means that it correctly did not add it to the repo and it threw the exception because the exception was not null and the valid one was added successfully. And then of course we retrieved the things in the ripple properly. All right, so we can close that Test Explorer, and it was the green ticks here. And then if we look back in our code, what was seeing field is no seeing passing. All right, so you can see how many tests are making reference to this particular handler method in this command. So that's a quick and dirty overview of unit testing and how it can help us with our code coverage. And cordial, of course, the red although tests for your other handlers and explore how the different scenarios can be tested for.
26. Setup ASP.NET MVC Project: Alright, so we're setting up our project in this new module. And what we're going to be using for our UI project is the ASP.net Core Web App, MVC. So our client application or UI could easily have been built with any technology that is capable of consuming a RESTful API. So we have our API that we already set up. But when we're going to be creating this client, We could have used Angular view, react or larva or blazer, any one of these. But I'm really going to stick to the MVC because it's rare that you actually see an MVC up being the consumer of an API. It's usually the all in one anyway, that, and the original project was already built-in MVC. So we're going to kind of keep that MVC feel to it. No problem. But then the underlying architecture has already been gutted and setup. So we'll go ahead and you can just search MVC. And remember that it's an ASP.net Core Web up, not the ASP.net Web application. All right, so you hit next, you give it the name, which, in which case we're calling it h i dot leave management dot MVC, and we are using a dotnet 5 project. You can also enable razor on time completion while you're here. And then you can just hit create. Now once that's done, you're going to end up with this project, which is the standard MVC template boilerplate for a standard MVC application for dotnet Core. You'll see that it looks a lot like our API project, except that it has a few more folders like views and models. Will MVC Model-View-Controller. And then we have the same startup file, the same program.cs file, the app settings. All of those things are kind of Common Pleas. And we have the ww www root folder, which has all four static. So if you're already familiar with MVC where you have no problem, but I will try my best to be best to be as detailed as possible with all the changes and the code that needs to be written and how everything ties together. So when we come back, we'll look at how we can start integrating our API into our clients.
27. Use NSwag for API Client Code: Alright, so we're buck and artist to be is to set up our client application to consume our API. So we already created our MVC application and we have our API documentation in the form of the sogar noch. So we'll be using n swapped to help us to generate, it's literally generates the code that, that would help us to consume this API. And between swagger and in swag, you'll see that in a few clicks, in a few minutes, we will have all the code, or at least most of the code needed to actually establish communication between the client and the API. So step 1, bring up your Swagger documentation and then you can head over to the JSON file that it generates by clicking this link, which will bring up this JSON file, which is basically powering this display that we're seeing here. So the next thing I want you to do is go to Google or Bing and find the GitHub page for n swag studio. Then you can download and install that and it's well-documented, but we'll be going through exactly what you need to do to get it up and running. So you can just download and install it. And once you have done that, you'll be greeted with this beautiful interface. So there are few things that you want to do too. Get started with this procedure. Number one, you want to make sure that the runtime is correct. So by default, I think it may go to net Core 21. We're using net five, so you can just go ahead and change that. And then the specification URL needs to be the URL to the JSON file coming from swagger. So you can just copy that from the browser and paste it there. Click create local copy. And then you see that documentation up here below. Now with all of that done and just the notes that in swag those supports other types of client applications, meaning TypeScript. If, if you were using view or reactor, one of those JavaScript frameworks, then you could just as easily generate code using and swag for those types of frameworks. However, we're sticking to the C-sharp clients, so you would just take that and you get that new tab. And then there are a few settings here. Let me just scroll to the top. There are a few settings here that we want to make sure are in police. Number one, setup the namespace. So for namespace I have HR dot leave management dot MVC dot services. That's the namespace I intend to have all the code generated into. There are other things that may be ticked already, but I'll just go through and make sure that we set up the card F1. So use the base URL for the request. You want to make sure that's ticked as well as generates the base URL property must be defined on the base class. Alright? You want to make sure that you have the inject HTTP client by a constructor ticked and generate interfaces for client classes. Scrolling on, I think most of these are already there by default. You want to make sure that you have the generic detail types ticked. And near to the end. We want to also ensure then we get to the end actually, because I think everything else here will be already filled in by default. So I'm not going to spend too much time on things that are already there and not I'm not tired, not absolutely necessary. So anything that's off point, it'll just make sure those are in place. You can leave everything else. And then at the end of it you want to put in the output file path so you can just go to your project correctly called the project. Lots of Visual Studio, sorry, grep, click on a project and go to Open in Explorer, take that URL or that path and paste it here, what we're putting services slash, slash client service dot CSS. So after you've done all of that, you can go ahead and hit Generate files and it shouldn't take very long to generate those files. And then when that is done, you can jump over to Visual Studio and go to your services base and service client class. I have some faster that we're going to discuss later on, but it's just focus on the server and service grant file. So we have this client file that gets generated and it gives us an interface and the implementation of the interface out of the box, right? Lots of documentation on annotations don't really have to worry about too many of those things. But at the end of the day, it generated a boat 1700 lines of code, at least in my book. So based on the size of your API, you may get more, you may get fewer. But the point is that all of this was done for us so we can focus on more important things, right? So we want to work smarter, not harder. And other important thing to note is that within swag, anytime that documentation changes, meaning somebody made a change to the API, swagger. As a result, got, albeit at the endpoints requirements changed, whatever does that change? You can always just delete that you're putting into. And so again, regenerate this file so you don't have to get that reduces the back and forth, especially in an agile environment where changes are inevitable and rapid. So you can always just keep up the pace with regenerate in this client and changing the code around it as necessary? No, I didn't mention that I have a few other files and while we will get into the details of them, I'm just going to show you. So I have contracts and the contracts that I have a contract file or an interface per endpoint type that I know of interacting with and also a service implementation file for each contract avons started for Windows in, but I'm just giving you an idea of what these additional files are in the base file, base folder. I also have an API response and they're more PFAS that will go into the base just so that we can have that extensibility. But for no, you don't have to worry about it. The next thing that you may want to do though, is set up Newton's soft. So you're probably getting an error anywhere that the word Newtons of these being referenced in this file. So you can always go to New gets. And then you would want to look for Newton's soft, sorry, Microsoft dot ASP, NET Core, VC dot Newton soft JSON. So you can go ahead and install that, do a fresh build, and those areas should disappear. Next, in our startup class, we want to register our HTTP client. So you say, in the configure services would say services that add client. I lead PaaS in IE client and client. Those two are probing for more generated file. And then we want to pass it the base address. So it's just a CEO or equal use client, you know, that's a lambda expression. Dots base address is equal to new URI. And then we want the URL to the actual API. So to find that URL API, what you can do is go to the API project, expand properties and launch settings. And then you'd see that SSL port. So it's basically http localhost colon for full, in my case, 44, 3, 27. In your case, it might be different, right? So HTTPS localhost and that SSL port as you see it in your lawn sittings. So with that done, we've actually set up the client or at least the foundation for the client. So the contracts and the service definitions that we have here and we will be fishing ultimately come back, are relative to the specific operations needed per call. Because when we call C gets all leaf types. How are we going to display the leaf types in our application? We still need to have models. We still need to de-serialize it from JSON because, you know, rest APIs they're sending and receiving JSON. So we'll need to convert from JSON into some type that our client application can appreciate. And then we can allow the user to interact with it accordingly. So when we come back, we'll look at all of those. Although coding activities that we need to put in our own, our client.
28. Setup Custom API Clients and Base Code: So now we're going to be creating or going through some of the supporting files and functions that we're going to put in place before we continue with building out our client application. So these are really onetime operations that we're going to be going through and I'll explain why they are helpful. Not necessarily necessary, but at least helpful. So let's start off with our third-party libraries in new gets. We're going to go and get autumn upper and we're going to get local storage by Julian Hansen's. All right, so that will help us later on. We're not necessarily going to use it right now, but you can just go ahead and get it from No, so that we can just set everything up once and for all. We're getting both autumn upper and ultimately extensions. And we already have the Newton's soft. So with all of that installed in the startup, we're going to make sure that we add the auto mapper. Now you're probably wondering, okay, why am I adding ultimate paper? Again after we probably did it already in the application. So we had set up this services for distribution in our application layer and we had added autumn oper. So you're probably wondering, okay, just like with the API when we just called configure application and services. Why don't you configure application and services that are Clint? Well, the reality is that the client really doesn't know anything about anything else in this project, right? So it's its own project. Imagine if we were using say, a view or an Angular application, it will be the same thing. There will be no, yeah, apart from the fact that there would be no bottom-up, but the fact is that there would be no core or infrastructure references. It barely even references the API, just knows that an API should be found at an address, and that is the assumption it makes. So with that said, that is why we're not referencing any of the other libraries inside of this client MVC application. It's standalone and all it does is reachable to an endpoint and expect a certain result afterwards, which would be the API call which we have configured here. So we're adding autumn upper individually to this project. And once again, that's because when we're getting the DTO with, we might have to massage data bits before we displayed to the user are interacting with it in any way, shape, or form. So we add auto mapper, and I've already gone ahead and added that. Well, you don't have to worry about that line will be going through these three lines in detail in a few. So just go ahead, add autumn upper. And then the next few things that we have to put in place would be to create a custom response type. So our response type here would be used when we talk to the API and the API response, it could be with the command response. It could be with the RBS command response, rather it could be with the validation errors, whatever it is. We don't know. Remember, we're only interacting with an API, so we're either expecting good stuff to come back or some form of exception are some fire of butter spawns. We don't know. So we're creating our own generic response type on the client, which will be capable of getting a message. It will be good. Love getting validation errors in the form of a string. It will know if it was successful and it would get back some objects, objects. So I'm calling it t go, we don't know what it could be. It could be an object with the you know, we were returned the object in some of the create, it could be an integer, would just the ID, we don't know. Of course it would be prudent of us to kind of backtrack and standardize all of those things. For educational purposes, we would have gone through different scenarios. But at least going into the more enterprise setting, we would want to standardize that so that we can definitely know what to expect for each scenario. So that is our response stuff from the client. All right, so moving on in the next the next file that we have that wants to look at is I claim. So in the base folder we have a client and you have clients that you're probably seeing. Okay. But didn't we see AAE client and the client before? And if you are seeing that, then you are correct. In our service clients that was generated, we do have ironclad and we do have client. But then we just want to kind of creates a little extension of that so that we don't have to dig too deeply to get to certain things. So one, you'd notice that this is a partial. Alright? So the cool thing about a partial is that it means that you can have multiple versions of the same class. Of course they're all going to work together. But any kevin have multiple files with the same name, the same class name, or interface name. And it's like they all merge at runtime. So it's pretty cool if you've never interacted with partials. But we have the partial being generated for us already. So we're just going to create our own partial implementation or our own partial AAE Client Interface, which just has access to the HTTP client. All right, so in service then we have that HTTP client. It's very deep. In times probably not very accessible, so we're just creating a public version of that. And then in the implementation which is public partial class credit in the, in haircutting, sorry, from Iceland. We just have that public property which is getting our returning that private property from the service kinds. So there's our private HTTP client. So that means this allows us not actually interact with the HTTP client in case you wanted to do anything to it. Like later on when we're doing security, we're going to want to interrupt with it's to put on headers before it sends off inflammation. So you'll see that in a few No 1 to note with the client and the client, you want to make sure that they're in the same namespace as a service, service gradient. All right, so actually when I was doing this, I did not have the dot because I gave it nbc dot services. That's the namespace I give to in swag to generate this file at even though it's going in the base folder. And then I client was automatically created with the namespace dot ds. So you have two options. You could either in this one to this, and then make sure that in swag knows that namespace should adopt these. Or you can just make sure that this is services because if they're not in the same namespace and they won't see each other as partials. So you can just take note of that when you're doing it. If you're getting any errors that you can't figure out why you're getting them. Now we're going to go back to our Contracts folder. And in there you'd see that I have a file called i local storage service. No. I'll talk about going forward. The local storage services for we will be implementing authentication using JSON Web Tokens or JWT for short know, after a Tolkien has been easy to application, application here being a client application, we need a way to store it in between operations, so you login and then while you're logged in, you're probably looking at your leave, you're probably doing a number of things. Anybody can be doing anything at any point in the system. But we are going to require authentication for you to be able to complete certain things. So a token definitely needs to be present. Like I said, we will be adding that later on. But I want to set up this at least the eye local service, local storage service from null because it will be needed for some other beast setup that we want to do. No. So after you get that local storage library from NuGet, we are just going to create an interface where we can abstract out some of the operations into our own methods. So we want clear storage exists, get storage value and a set storage value. And it's really just a value key pair. So we're just going to give it a key and a value. So you can use this to store little bits of information. Of course, you don't want to store anything too sensitive inside of local storage because then it can open up your application for vulnerabilities. But that is its purpose. So after you set up this interface, you can go ahead and set up the implementation in the services folder. So we have local storage service inheriting from my local storage. And we have a field of type local storage which comes to us from the library given to us by Hansen's dot net. And here in the constructor we're just sitting up like a little config and then initializing storage. So our configure says new low-cost storage configuration. Auto load and autosave are true. And then the file name I just gave it that name that is indicative of the application that it belongs to. So you can go ahead and do that. And then storage is equal to new local storage. And all the methods are fairly straightforward. In the clearest storage, we just have the list of keys we want to clear from storage. So this coordinate handy when somebody's probably logging out and you want to remove the references to the tokens and whatever else you would have stored. You can just go ahead and pass in all the keys and it would just remove them for you. You have the set storage value where we pass in the key and whatever value and then it will store it. No, This is t. Yes, it can take like an object or a string, whatever datatype, but it will parse it down into some form of stored value and then storage that persist basic analysis, just keep it and make sure you're paying attention to this. Gets storage value basically just returns whatever it is based on the key that is requested. And then we can always check if it exists, right? So pass in the key and it will return storage Xist, which is just a Boolean. Yes it exists or no, it doesn't. No one step that I would have overlooked tours to add this to the startup files. So we have to say services dot add singleton this time because we don't need multiple instances of our local storage. That would actually be. Pretty bad idea, right? So we just want one instance of the local storage to persist for the entire user, the user runtime or the usage of this client application. And we're just passing in the eye low-cost storage service as well as the local storage service? No, In our base HTTP service, which is living in the base folder in our services. We're just going to be sitting up some base operations that we want every service to be able to do. So one where we have the fields for I, low-cost storage, storage service and a client which we initialized using our constructor, we're familiar with that or just injecting them in. And then later, Don't we have two methods. We have one where we convert API exceptions are it's so API exception is actually coming from our service plant cogeneration. Because in looking at the endpoint, looking at what to expect, it realized that we could get costume exceptions with status code and response and other things, right? So we have access to that base type, but then probably want to convert it into the response if it ever comes over. So we can see if the status code of the API exception is 400, then we see validation errors have occurred because that was the bad request, right? If it's 404, then we see message, the item could not be found. And then otherwise, if it's 500 and you see something went wrong, and as many error codes as we would have b, we know that we would be able to get back from the API. We can always set them up here and have customized, least to some extent, customized messages per era type. All right, The next method would be to add the beer Tolkien. So protected void at bearer token allows us to see if the low-cost storage has some value with the word Tolkien. Some just once again, Linda, Linda grown work here, right where we're not quite ready for this one. I'm just putting it there and we'll discuss it in detail later on. But this method allows you to add the bearer token to the client dot HTTP, client defaulted or requests authorization, right? So all of this code is basically how you get access to the authorization headers in any HTTP client communication with our RESTful API. And what we'll be doing is adding a new authentication header value. Let me just break line here sits or clear. So we're adding a new authentication header value with the name bearer. And we're putting it from local storage, get storage value of type string with that key. Alright, so that's your soul will be getting the tokens to send with our HTTP requests. But we're not quite ready to discuss this in detail, but you can write in the code from null. Once again, all of this is grown work. No one we're going to be looking at our costume. I don't like calling them costume, but our extensions of the different features are the different kinds of interactions that we're going to be having with the API alike, all of the leaf type interactions, all of the requests and allocation interactions. We have obstructed them into their own contracts. And then all of them will be interacting with the base HTTP service anyway, because when we secure up, they will need certain BCE functionality and for general interruption. Alright, so when we come back and we start looking at setting up the leaf type, because that's always the easiest one, right? The one is fairly straightforward. So it starts off with that one. And I will look at how we tie everything together.
29. Setup Leave Type Management Service: Hi there, Welcome back. We're going to start looking at the leaf type service requirements. But before we get into the contract and the service implementation in the UI project, I want us to do some housekeeping things with the handlers and the API. And I'll explain, of course as we go along, why these are beneficial before we move forward. So to start, let's just jump over to the create leaf type command handler. And if you need our first heard that is in the the application project onto the core folder. All right, so what I would encourage you to do one is to standardize what is being returned. So at all points, we want to probably have the bs command response. Remember that a bs command response allows us to pass back the success flag, the ID of the new record, as well as a message, right? So you'll want to refactor your handler code. I'll just leave this on the screen long enough that you can pause and refactor accordingly, where if the validation fails, then the response successes falls. They will have a message and we put in all the validation errors. And then we have else. If it is okay, then we have the true, the message and we return the ID with the response object. Of course, if we're updating that to return bs command response, that means that our request needs to also return that. So you can just go ahead and update the leaf type command to have I request returning bs command response. So that's sums of the changes that we need in the handler as well as the command class. Though. Moving on from that, let's jump over to the API. Now back when we were discussing the API, we also mentioned why it is a good idea in the task action results to also include the datatype being returned. And we saw in the swagger documentation that, you know, for the ones that had our return type, the documentation actually clearly stated what would be returned. Now that we have adjusted the return type for our create command handler, let us also adjust the action results return type for the beat with the bs command response. Hard because then when we do that, we get the response, we return okay. With that response. All right. So that we'll know, enhance our documentation and then I'm going to show you another thing that makes it even better in terms of what the documentation being up-to-date and being that explicit helps us to do. Now one more thing before we move ahead, I want you to right-click on your test project and run tests. So just in case you weren't convinced about why unit tests are important. If you run the tests, you'll probably get all failures for the tests that were to check if the, if the validation or the valid leaf type or invalid leaf type were added successfully, Why are they failing? Well, we just made our refactor, so we just change something in our code, our Teslas chicken for one thing. And obviously what we changed would break that test. Now if you go back to your test, remember that we said the result of the ADD operation should be of type int. So once again, this is a great way to see where refactoring your code would actually modify things, right? So no, the valid test would have said should be leaf type int. We're going to change that to should be based command response. Now the invalid one was counting on a validation exception being thrown, which was the case before or factored or snow. So we actually need to adjust this test to know reflect that There is no exception one. And that we are just doing the same kind of operation where we're just calling waiting for 0 because that is what our new code is doing, right? So we're calling waiting for results. And the results should be off the type based command response and the code should be three. So if we rerun those tests, then we see that we have successful tests all our own. So once again, unit itself was to keep us, helps to keep us in check with our factors and our core changes in such a big project. So now that we've modified or API, we need to regenerate the Cold War, the service client. And in addition to just regenerating, there are a few changes that I want to point out with the settings as you do that. So once again, go ahead and get that JSON link if you didn't have it up already. And then in the end swag interface, we're going to paste it and we're going to recreate the local copy. So we have to do that step every single time, whether you had it there are not because whatever it sees in the JSON is what it is going to use when it's generating the code. So I'm pointing that toe because before the post method that we just modified only had a return type of 200, 100 with maybe an int. No, you'll see that it has other return types. All right. So the inference from that for the end SWACH Studio is that it needs to compensate for the fact that it should expect our return type of bs command response. So we're going to be the same thing. So if you have it open already, that's fine. But if not, we can go through it again. It's R-dot leave management, but MVC dot services.js, I'm putting on the base this time Mr. it off the first-time, want to ensure that we're generating the exception classes. Inject HTTP client via instructor and generate interfaces for Client Access. And we want to wrap the detail exceptions in soccer. Exception instance. We want to generate the details. But one thing that if you had before, I would love for you to untick Is this on a boat, the US base URL for the request. So initially we had ticked it. You can untick it. Reason being in the service grant code that you have opened all, you would see that there is a baseURL field in there and it's waiting for a string. So we're actually kind of bypass the need to put the string in that code because we had registered or HTTP client with its base address. So we don't need that. Use baseURL for request anymore. All right, so you can untick dot one and go ahead and make sure everything else is ticked accordingly. And once you've done that, of course, you pointed to the output path and then you generate files. So once you that that service trends CS file will be regenerated and updated to know expect that new return type or types if you refactor more than one things, whatever it is that is new in your API will be reflected in the documentation and as a result, reflected in your new service brand class. So now that we've completed the groundwork with our handlers and our API. Let's jump back to our UI. So I'm in the startup class and Lee can see that I've uncommented the I leave type service leaf type service line. We will uncomment the others in due time, but you can just start with that one on commented. And let us look at the implementations. Before that though, I want to point out that we do have the mapping profile. So I created the mapping profile which follows the same procedures were used to from our application layer where we inherit from profile courtesy of autumn upper and then we start putting in these profile map configurations. I also have created some view models. I'm going to actually start with the viewModel before we look at the leaves service implementation and interface. So if we just don't know where to that, I have created in our models folder, leave type VM. I just put everything in one file this time around. So in the leaf type VM, you'll see that I have that, which represents the VM that has all fields, but it's only has the ID. And then I have created leave that VM where we have the name and the default is, so leave that VM inherits from create leaf that VM. So we always looked at how can we reduce repetition between the VMs. And once again, it depends on how granular you want to get in terms of the data or the data points rather that you're putting into your view models. So here, leave that VM has ID and inherits all the other fields from Create and creates those not have that ID. And we already discussed it's from the detail level, why that is done that way. So once again, from the mapping profile, I can map like to like between the ViewModel native to the, to the UI application or the way and the detail courtesy of our AI service client file or service grant file, sorry, where we have all the details kind of be made reference to in that file. So you can go ahead and replicate that kind of mapping and those models. Now let's jump over to, I believe, type service. So this interface, it's in contracts and it basically implements all of the crowd functions that we know are native. You need to I leave type operations are believed type operations. So we have the get leaf types which is returning a list of lift, IBM, the one that gets the detail returning the same VM. We have task response insular. They looked at the response class. So here we actually expect that response relative to the Father. We're getting that base response from the command that is creating our leaf type. So our task is going to return a response, but then interior, this represents the id value that is coming back because that's what we're really, really want, right? So a response relative to the type of int create leaf type and we're passing in that leaf that VM. We also have the task to update and a task to delete. So after we've set up all of these in the interface as we've seen before, once you have a contract, you have to have the implementation. So over in the implementation which I just put in the services folder, we have the leaf type service. So let us go through this one together. So this is inheriting from the base HTTP service which you went through together. And then I leave type service at that point is going to tell you all you need to do the implementation. No problem, you go ahead and let it implement all of the methods required. So inside of this leaf type service, we have the local surge, the IM upper and AAE client being injected. So all of those are injected and initialized accordingly. We also have to pass over to our base. And base here being the base HTTP service, the HTTP client, as well as the local storage service. You've seen that already. So you have to set all of those things. Please point to stop there really for this segment, when we come back, we will look at the different methods that were implemented. I have some code there already, but that's fine. We'll go through it in detail on the comeback. But these are your methods being inherited from the elif type service and they're all put there waiting to be implemented. So we'll go through that as well as our controllers and our views in the next lesson.
30. Setup Leave Type Management UI: In this lesson, we're going to be walking through our controllers and views and the general user interface on how we lead our application interacts with the API for all the crud operations. But before we get there, I have some corrections that we need to make to our interface and the implementation for our leaf type service. So corrections would include updating the return types for the update and delete. I kind of got copying and pasting. And in the previous lesson, you'd have probably copied over the incorrect implementations are incorrect prototypes for these methods. So you can go ahead and update these accordingly, where they're all supposed to be returning the same task response relative to the type int. And for the update and the delete update is good to take an int id parameter and the leaf type VM. And the delete is going to take that int id parameter. So you can go ahead and make those changes and ofcourse update the implementations inside of the leaf type service file. Now in order to start making the user interface, of course, we have to put the underlying implementation and then wire up the code and everything. So we'll start off with the very simple ones, which would be the leaf type R to get leaf types, which will be the list. And they get leaf type details, which would be the one. Alright, so let us look at the implementations for those tool. They're pretty simple. All we're doing is initializing a variable I'm calling in the leaf types or Napoleon via leaf types equals await client. So this client is not the HTTP grant that's being injected in what the client coming from obese HTTP service, right? Granted, it would almost be the same thing because the only reason we have this one is so that we can pass it over to the base. All right, but let's just stick to it. So underscore client represents the base issue HTTP service gland. And that's what we're using. So I'll wait on those Work Trend, dot leaf types, all a sink. All right, so that is giving us access to all of the methods that were generated for us in the service client thunks the end swap. So you'll see leaf types of leaf types, leads get fullest is seen by somebody named him methods in your API controller, you may see slightly different names, but I just looked on particularly naming convention for standard API development. And I'm just getting these. So I think it's clear enough that this one is getting all of the other one is getting, just want to get, et cetera. Either way, the parameters can always be your guide. So Vardy types is equal to await all the leaf types. And then we return mop, mop list, leaf type, VM, the leaf types. All right, so that's why this is returning that list of leaf type VM, because this would return in the list are public zone of leaf type DTLs hour just mapping them to our local leaf type VM. So we already looked at the mapper and the configuration for that. No outside of that, for the details, what we're doing here is getting only one. So we're seeing var leaf to be z equal to 08. Client. Leaf types get a sink and the gets takes up our mentor off int id. And then likewise we are returning a mock version to leave type VM of this leaf. Now let's go to one of the more complicated ones. Let's start with the delete leaf type, and I'll delete leaf type only needs the ID. It only needs the ID because the API parameter when it needs the ID. So we're doing a trend catch in this situation where we're going to see a weight client.read leaf types, delete async passing in that ID. And then we'll return a new response of type int where the success is true. All right, So if that was not successful, then that's why we catch and then we convert or API that would have our API exception rather that would've been coming back into the response with the appropriate information. Alright, so that's pretty much it for the delete. And then the update. Actually it looks very similar to that except towards taking ID and the leaf type in as type leaf that VM. Then I'm going to do a mapping to convert that from leaf to a VM into the leaf type DTO before we then send it over. And all the thing about this leaf types put async. If you hover over it, you'll see that it takes an ID of type string, my end generated string. I found it surprising what I just said, let me work with it instead of trying to work around it. Yours takes int, then that's fine. You don't have to do this id dot to string, but in my case it takes string, so no problem, I'm taking the int id, I'm just converting it to string. And I'm also passing over that leaf diabetes. Well that's I just converted. Then if everything was successful, we just return a response with success being equal to true. Once again, it's in a try catch. So any exception would be caught. Know with the create puts you in a bit more. So I started or you would have seen a preview of this. Well, let me walk you through what this code is doing this time. So once again, it's returning a response with dive into staking, create leaf VM as its parameter. And it's really the same bits of code, but just with a few more plots and twists to it. So here I'm seeing create leaf type detail and I'm doing the mapping. And then I'm seeing Get me the response from the post. So the reason I have to do this is remember that we updated our API call to return this command response. The other ones I returning null content, right? So that's why there are only tasks. But in the case of the create, we're actually getting feedback after the call. So we're getting a better response, which is bs command response. So service client actually generated this glass for us to mimic the base command response coming over from the API. So remember at this point we still don't know anything about AICPA were not interacted with the actual base response implementation that we did all the way up in our application layer. All of this is being done right here in the client up. So basically non-response helps buck after we make a post attempt. And remember that's opposed to attempt, even if it feels it's not necessarily a failure because he could have failed because of validation reasons, but would still be getting about 200 response, right? So the try catch, and just to backtrack a bit, the try-catch here is really that if the service client is set up in a way that if the response is not a 200, then it sees it as an exception, which is why we do that conversion. Know in the case of the, the Create, we could have gotten a 200 response with this base command response, but then it's not successful. So we're getting back, okay, http wise. But the operation itself was not successful, so the engine is telling us that it was not successful. We wrote that engine so we knowable that right. But in the case that you are consuming a third party API at us, at this point, they would have had to make it clear to you that you could get a 200, but you always have to check this flag if it's true or false. So in this case, if it is successful, then we outfit our response, that's our local response of type int. We outfit the data with the ID. Remember we talked about that, returning the id of the newly created record, and then the success is true. So locally we know that our attempt to create was indeed successful. Otherwise, we're not going to get all of the arrows that would've been packaged in that base command response with those, with the validation errors. And then we're putting them into our local response and piling them up. And then we're returning that response so that it was true or false or returning that response. And once again, we do catch any exceptions that would have been thrown. So now that we have a walkthrough all fought, our service implementation code looks like. And of course this is still permeate to changes in an agile environment. You may do it one way and in ginger down the road because there's something else that's fine. But for now that's what we have. So the next step would be to start creating our controllers. And the controller that we're starting with would be of course for leaf types. So you can go ahead and right-click controllers, click Add controller. And I'm going to just do an MVC controller with redirect options. Go ahead and add it, and we're going to be calling it leaf types controller. So know that the controller has been created. The first order of business would be to set up the injection. So we want to inject our I'll leave type service and we can, of course, go ahead and add any missing references. So I leave type service is null in our controller. Let us start off with the easy, I'm going to say the easiest pages to get up and running. How we can start off with the index page. So easy way to create the index page. You can just right-click, click Add view. And we want to raise their view index. We want the list template. And then it should be a list relative to the type, leaf type of VM. So you can go ahead and add that. Once you've done that, you'll get that view. All right, so it's nice and simple. And if you're used to MVC, then this is no real task for you. So you know what this is. And because we're not using an EOF, our domain object, certain things we love to wire money, whether this section where it's asking for the primary key, so say item dot ID. So that's why we use the leaf type VM, which has all the fields as opposed to the creator, the other ones, because we definitely need the ID for this sake and go ahead and update those links so that edits the details and the delete. Know where to get the ID from when we need to navigate. If you want to spruce up the user interface from No, You can also remove the id fields because you and I know that we don't necessarily need those in the daily operations of displaying data. Know that the interface is created. Of course, we need to let the interface N4 where it's getting its data from. So this call is going to be pretty simple, where all we're going to see is VAR model is equal to weight. The leaf type repository git leaf types, which is what we just looked at the implementation for. So it's calling it leave types, which then causes API and returns the mopped version. So once again, we're promoting slim controllers here because it'll wanted to be doing all of the logic here, you know, doing the API call, doing the mapping, and then return in the view. We want to farm that all to somewhere else. So the controller just knows, call this method, get the data, and then carry on with life. This red line is because we need to make this method an option results. That's a task and a sink, right? So if you wanted to get actually just go through and make all of these because they're all going to be making asynchronous calls. So just to reduce the likelihood of getting these arrows, just go ahead and change everything to an AsyncTask of action result. And then that is it for the index. So let us take this worse been before I do, let me go to the layout and add a URL for it. So I'm just adding a new menu item that says is B dash controller leaf types is the action is index and it's def types. So let's run and see. I'm sorry, before you run, before you run, I failed to mention that we need to let both the client and the API arrow and simultaneously. So what do you need to do is right-click this solution and go to Properties. And then you would go to startup project and change it to multiple start-up projects. So we need the APA to have start. We need MVC to also have start. If you want me to start with our debugging. So it takes, you need a shorter time to start off. Secant is go ahead and click Start. So now our app is up, it's running and all we need to do now is jump over to leave types. And there's our data that is coming directly from our database. Well, we don't know. We don't care at this point where it's coming from. We just know where calling an API, asking it for the data and it, we're displaying it here. So everything is happening out of the box, right? Of course, in the background, as API developers, we know all the words that we put in with a handlers and everything so we know where the data is coming from. Both our client doesn't know. All right, so next up we want to be able to create. So we need to create our scuffled that creates view. To do that, once again, all we have to do is go to View, right-click Add view, razor view. And then we want to create templates. And the model here would be the Create leave type VM, and then you go ahead and add that. So once you have added the view and you get the physical file, the code that we need to write here is twofold. So one for the get, we don't need any code because we're only loading the form, right? So it's already, it's already created that form knowing that it should be relative to the fields that are here. So we have the field for the name of the fetal for the default days. We don't need to do anything else. So unless you have something special, that's fine. But at the basic level, nothing else is required. However, on post, that is where the magic needs to happen. So what we need to do now is to our weight, our call to the leaf type operation. And well, before we even get there, we need to make sure that we're looking at the correct datatype, right? So I'm going to call this create leaf to VM and leave type. Go ahead and include any missing references. So create leaf to VM leaf type. We're going to try to get a response from the leaf that repository dot create leaf type. And then if our operation is successful, then we can redirect to index. All right, so look at this null. If we do this call, If it was a success, then we can operate like it was a success. What happens when it failed? So when it fails, we would want to, of course, add our validation errors to the user interface. So the arrows would have come back with the response. So you can say model states, model error, response validation errors. And at the end of all of this, we just return the view. So we're going to modify it a catch to not return the view has a muddle fact I'm going to modify the catch, the catch an actual exception, and then we can add that exception. So this is at your discretion. Of course, this depends on the experience that you want your user to have, because you may not necessarily want them to see the details of this exception. You could probably just put in something that says something went wrong, please contact your administrator. But at the end of this operation, you do want to return the view. And you can even put back the leaf type data that was just submitted. To the view so that it has whatever to display to the user. So let us take that one for a spin art so we can run the application, jump over to our Create. And I'm going to create compassionately with the default number of these Sten. Well, let's say I made a mistake and I put in too many zeros. And then I click Create. You'll see here that we're getting back our validation message. And I'm sure this message looks very familiar. I haven't typed this message since I have been building this UI up. This is coming directly from the API. So this is just to show you how the information traveled from the API and how we could be split to the user that the APIC, This is illegal. Now you have two options when it comes to validation. You could want rely on the API for all of your validation and your messages and so on. But then the thing is that you at some API is charged per call. All right, so in this situation where you're probably building on top of an API that you don't want too much traffic or the API users tell you did on too much traffic or your limited or something. Well, you could do is use the documentation from the API and enforce the validation from the client side. So this message is coming directly from the API. Sure. What then that means an API call was made, some process was done, and it shot buck with that message. All of that could've been avoided if I read through the documentation as the client application developer and set up my own validation rules from the client side. So then this would never even get to the API call until this is valid by the client standards. Now, there are mics. Based on your intuition, you will have to do it one way or the other. But for now, we let the API do the validation. And then I think it's always good to build-in validation in the API because that's the last line of defense before, or at least in the hundreds rather, that's the last line of defense before it gets the database. So right there you want to make sure only valid stuff gets the database. You could, however, encourage your API consumers to follow the validation rules. So combustion at the default number of these 10 con, continue with the creation and look at that. We have a brand new leaf types, so the creation worked and then a loaded, reloaded index page and we're back here. Don't mind the ID. Some experimentation went on. Buck in both at least we are confident that this is working. Now, somebody came back and said compassionately should actually be 15 days. And all we need to go ahead and edited. And all the first step is to actually create the view. So directly view at view edit. And we know that we're using the edit and the leaf type VM. And then once you have all of that in place, you can go ahead and add. And then in the edit you notice, just like with create, we have two methods. We have the post and we have the gets, knowing the Get. It means that it has to get the required to be edited. Well, how do we get that record? We already know how to get a list of records. We have looked at getting one record just yet, but whatever cool we have the right to get the one record here is pretty much the same cool to have to write to get the one record here. So what we're going to do is say var model is equal to 08 leaf type repository dot get leaf type details. So details would bring up all the fields that are needed for an edit operation. The ID, the, you know, the fields, all the fields, the name of names of the fields are eluding Marino, but we're getting back all of the details for the leaf type that's he's about to be edited, then we're returning the view with that data. So while I've done this year for the edits, I'm really just going to do it again for the details because it's the same thing. Details. We get the details. We get to ID, pass it along, and then we return the view with that data. All right, so we go ahead and do that, edit for the get. And then for the post is going to look a bit similar to our Create except our parameters should have the int id and leave type VM. All right, so ID leaf type VM and they were trying to get a response from our update leaf type. And then we pass in the ID and the object to be edited. And if it was successful, then we returned to the index. Otherwise, we basically just daisy chain and all of the validation error, just like we did with the create. And then we return the view with the data that was submitted. So let's start the edit operation and off the bus you can see that there's some fees that we don't need. For instance, we definitely don't need to display the ID in this fashion, in an additive fashion to the user That's not even going to work. Or API would reject that altogether. The database would reject to spread the name. Default number of days. That's all we said. We needed to change the 15 and I'm just curious to see what exactly is going to happen if I attempt this, if I click Save, All right, so I'm getting this Mrs. of value cannot be null. Value cannot be null. Okay? Let me change this back to 10 and let me click Save. All right, so that worked though. Let me revisit what that message meant. If the data is invalid. Do remember that our handler that is dealing with the update, right? No, it was, or at least originally was designed to throw the validation exception when the data was invalid. So jumping back over to that handler does IQ and see what I meant. We didn't return anything. It was just the unit or at which we explained was just something to say. Everything is okay. But then when the validation result was invalid, we threw a new validation exception. Now this validation exception has not been handled. We haven't written any real quote to handle what happens when our handler throws an exception hold the API deals with it and how everything else comes down. So between the handler throwing the exception, the AICPA and auto being global error handling to know what kind of responses in buck. And then the service grant not being able to fathom what kind of response this is, then we're getting this kind of error. So technically, you wouldn't necessarily all you wouldn't at all wanted to use it to see an error like this. This is actually the error coming back from that whole try catch and the exception message being thrown back into the model state validation. You wouldn't necessarily want to use it to see that. But later on when we look at global exception handling will see how we can better or provide better feedback when exceptions are thrown through the handler or through the API all the way back to the user interface without us having to compensate for too many scenarios. All right, but for an all we actually see that the validation is working to some extent it is failing because if I put a breakpoint here in this handler, and then I click Save again, Oh, nuts in debug mode. Or we can do that later on, but you can try that does put a breakpoint inside of the handler. Try that, see if you'd see that it hits the validation is not there. Sorry, validation is false. And then it will throw the exception buck. And if you step through and follow it down the line, you'll see why all of this just amounts to this very vague message. However, we do see that the edit is working properly. And next stop on. We already looked at the detail so you can go ahead and generate the details view. I have mine already, and you'll see that between the code that we just put in the controller and you're generating the view, you can view the details. So the next one would be delete. Know for the delete usually stored in a two fold step in MVC applications, right? So usually you get the record and show it to the user like in a details view and say, are you sure you want to remove this record? And then they would say yes, and then it basically submits a form. So this is a very secure way or the better way to do it. Let it submit the form. So a port it by the anti forgery Tolkien and it would carry over that delete operation. No, what I would do, or there are number of ways to approach this. But what I have done multiple times is instead of using both the good and the post, I just have the post, right? So this is what my delete method looks like. And if you look at it, you see that it looks very much like the, everything else that the editor and the Creator other. So we try, we get the response. If it's successful, then we redirect to the index page and then I'm just returning a byte requests. So the beach, the application would just feel, but we'll look at how to handle that later on when we look our modifications for null, however, on the index page, what I would do is instead of having this axon link, I would actually wrap these in a farm. Once again, there are many ways to do this because some people have the form going through and through. Some people have one form and use JavaScript to see it when you click the Delete button, then delete the farm. But right now, I'm just going to keep it simple enough to get through this. So this is a delete button. All right, so I didn't want to bore you with my typing, so I went ahead and finished the top. So our form looks something like this, where form is Option, Delete is B dash, route ID is item.name. Using that at sign for ease of use. And method is equal to post. So between these three will know that it should target the post method of name axon and pass in that ID. So then it knows post and delete with that ID. And then what we'd need to do is put in the up button. So button type submit and I gave it the class btn, btn dashed line. So it looks like the other two links, so it looks uniform. Nobody would ever know that it's abort and unless they really go into the code to see. And then onclick, we are returning upon firm window. Are you sure you want to delete? And then we pass in the word delete. So let's take a look at what this renders. All right, so this is what we get not quite lined up. We can fix that later on, but the point is that we're getting that delete button. So I have a very random one here. I think somebody was trying to spoof the system and they enter that, so that's fine. Let us delete it. Are you sure you want to delete this record? Okay. And then you see everything happened. That record is no longer there. So that means our delete operation worked successfully. And with that done, we have just completed crowd for our leaf types via our user interface. All right, So when we come back, we'll look at how we can complete the other specific operations with the leave requests and the leave allocations.
31. Add Json Web Token (JWT) Authentication to API: Hey guys, welcome back. In this lesson we're going to be talking about authentication. Now the next logical step following or setting of the leaf type UI would have been to settle the leaf allocation and leave request UIs. But then the spoken that plan is that in order to allocate leave or requests leave we need users or employees. So the only way we can get the employee sees the system is if they have logged in and we know who they are, That's his wife. Authentication needs to come before those other two features. So I've kind of gone ahead and implemented it because there's quite a bit of code to write and I don't want to bore you with watching me type. So of course I'm going to go through it very slowly so that you can pause, replicate, and explain everything that is there that needs to be understood. No. Outside of that, I have swagger where I have implemented JWT authentication, so I just have it up so you can get a preview of what JWT authentication really is in case you're not very familiar with it. So let us J, W, and T are short for JSON Web Tokens. And what they are is just like a package, just a flat string that is encoded. But within this encoded string are what you call bits of information are claims that allows the D chord and know who the user is. No JWT is widely used in the API fraternity as the standard for security because it allows for stateless authentication. Meaning, when you log in, you're not really logging into an API layer accessing the API. But if it is lot bone or protected and you authenticate on the API and they basically identify who you are and then send this token with all the information about who you are. So the client application now needs to see anytime I am using the client application and I request data from the API, it needs to include this token with my information so that every time the API gets called, when it sees this token, it will decode it, verify that this person is valid and then be able to execute on the request. So just a quick demonstration here, I'm going to try and login. So I've already modified swagger, I've already modified the API with the login and registration endpoints and we're going to go through that. But I have also put in some sample users. And I'm just going to go ahead and show what the token looks like. So I'm executing a login request with one of my users. And you see here it's returning a 200 with this token response, right? So I get the ID of the user, the user name, the email. And this is that JWT or token, right? So this is basically a vehicle with all the information that is needed to identify me. So just to show you what comes up in this token, I'm going to jump over to JWT dot IO, which is a cool free tool that you can use to just paste your Tolkien right here. It's encoded form and then to the right it will essentially everything that is in this token. So subject, that's the email address, GTI, that is the JWT identifier. Yeah, the email, you have, the user ID, the roles that exploration timestamp, the issuer, and the audience. So you can put in more things. These are just basic things that this token that we're generating is going to have. But going forward, you can put in more things and putting all the information that you deem necessary for your application or your API to function. Now let's get started in our code art. So firstly, I'm going to bring your attention to, and I'm just going to collapse everything that's not absolutely necessary to the, to the conversation at hand so that it's less confusing. So in our application project, you want to add a new folder in the models folder called identity. Know when adding this folder that says that I should have put the email and email settings in a folder for themselves, but we can do that later on. Put them in a folder called e-mail or mail, something like that. But for an all models identity and then we have these new files. So we have auth request, which is basically just email and password. We just saw that when we were logging in through swagger, we put in the email, put in the password, and then we submit it. So that's what the auth request is for. Auth response is what we got buck through swagger, the ID, username, the email, and the token string. All right, the JWT settings, this has to do it's similar to the e-mail settings. Remember that we want the e-mail settings of the part of the Up settings.js ON file that gave us all the settings needed for the e-mail. Well, that's what's going to happen with JWT settings. So we're going to have a key issue or an audience and a duration in minutes, all being referenced in the UP settings file in our API. Now just like all we have the auth request, we're going to have the registration request. So often is short for authentication, I just shortened it, but you can be explicit and say authentication. But registration request is going to require the firstName, the lastName, the email address, the username, as well as the password. So anybody who is registering on the up and the new employee who is coming into the company will tell them, you can still further stir. You need to provide this information. And then the redistribution response is really just going to push back with the user ID. So after this person has registered there in the system, we just send the user ID. That's all that's required. No, outside of that, we also want a new folder in our Contracts folder, and I'm calling it identity. And we want I auth service. So we're going to have a new service where we have task author response login. And it takes that request object of auth request. And then we have one for distribution which is giving us the registration response. And it has a redistribution quest. And that's really all that is required for the application. So you can go ahead and review if you need to. Of course, pause as you're going along to make sure that you're getting all of these files and the required fields. And notice the validation attributes that are on the registration request because we want to make sure that we're meeting minimum standards. Nor our next task will have us adding a brand new project to the ICT infrastructure folder called Identity Art. So HR management dot identities the name, and we would want this one to be a dot in it, five. So I'm going to explain that one. Yes, we're using dotnet standard for most of the class libraries. But in this case we're going to need certain libraries that just cannot work with a dominant standard library. So we want dotnet fire so that we have 0 compatibility issues. And of course this would carry on into future versions of it. So you can go ahead and add that new project. I already have it here. And in this new project, we're going to have a few new folders. We're going to have configurations, migrations, models, and services. We have new files where we have the Identity Services redistribution. We're familiar with that where we're sitting of that registration file to be called from the API. And we also have our own DB context. So I'm going to start off with a DB context because this is probably the easiest one. So in the DB context, we have public class leave management id, identity, DVI contexts inheriting from identity DB context. Notice here though that we are typing the identity DB context. So I could have done this, but I have a special override class for my users. So generally speaking, identity user doesn't have extra fees like firstName and lastName. So I have this application user class that I have created in the models folder. And it inherits from identity user. So then it has two fields. Firstname, lastName. In your situation, you may need your users to have more information on those first name, last name, and e-mail. And so maybe you want date of birth, you want other fields. You can add all of those right here. But as long as you're inheriting from identity user application user can be used to replace identity user and user related operation. Those give you access to all the fields necessary. So jumping back to the DB context, we have the same kind of constructor that you would have seen from our previous DB context, where we're taking the DB context options and that will be Passover via the API project in the upsetting once again. And then we have the on model creating. So this time I'm not doing that, gets assembly. I am calling the bees on model creating. And then I'm calling them one by one. In practicality really doesn't even matter. So let's just continue. So we have the rule configuration, the user configuration and user role configuration. So this is where I'm seeding those bits of data when generating this database. So remember I said that generated users before. So if we go over to the configurations folder, you'll see role configuration where I have rule configuration inheriting from I entity type configuration identity role. Now this one is set up to configure new rules. We have the ID, the name of the rule, and normalized name ID. This just needs to be at good so you can go and get our random good Dino, generate one online, it's fine. You can use it there. You don't have to use the same one. I have bought. These need to be good. So our identity tables don't use integers as their IDs. After you're finished with the roles, you would want to take care of the users. So users, there's a bit more to it, but it's really just a bit more because there's more to fill in, but it's practically the same concept, user configuration inheriting from an entity type configuration relative to application user. And then in our configure method, we have var Hampshire is equal to new password Hampshire, relevant relative, sorry to application user. So this is going to be used to hash the password. So we're filling in to application users once again gets a good, we're giving this one the email and email and normalized e-mail and username, unnormalized username, they're all the same. The only difference is that the normalized versions have all caps. If you're familiar with identity user, then I won't, I don't want to bore you, but you kinda see I'm filling in all of the fields accordingly. And then for the password hash, I'm using the hoBshare to hash of the password null for the first parameter. We don't have to pass in the application user. Obviously we can't because we're just creating one. But we are going to hash the password, right? So I'm just using my special default password that usually meets the minimum requirements to create our user in identity. So we have that one for the admin and then we have another one for a regular user. So after we have our two users under two rules, I have user or configuration, which is pretty much I entity type configuration at entity user role. And then you pass in that typing for a string. Very important. And then we have two new identity user roles. We have the role ID to the user ID, rule ID to the user ID. So this is seeing role, I think this is the employee role to the user that was supposedly the employee and then the role ID to the user ID. So because this kind of has to happen in order we need the rule we need to use are all before the user or gets generated. So I think that's why I just explicitly stated the configurations like this. So it wouldn't run this run that then run that. Now before I go any further, just wanted to 0 songs the libraries that you will need in this projects just so that you have on top so many red lines. So we want to make sure that we have a speed dotnet Core Authentication, JWT bearer, we want a core identity, identity Entity Framework, Core Entity Framework, Core dot SQL, the tools, the extensions for the configurations Newton solved dot JSON and system.out.print city model, Tolkien's JWT. So you probably already included some of those because in setting up the DB context and some of the other files, you would have been prompted that you need certain libraries. So if you have that already, that's fine, but those are the libraries that you will need overall. As you go along, you can just include the missing using statements and references accordingly. So let's jump over to the implementation of our Auth service. So we have the auth service in the Contracts folder. Now we have the implementation. We're used to the spine. All right, so we have three fields are, well, let's start off with the constructor and how we're injecting the user manager. We're injecting the sign-in manager and we're looking for options for the section called JWTs settings. So you can just go ahead and create those three fields. And then in our implementations we have login and register, and we have a few private methods, but we'll just go through them one by one. So in our login where taking the auth request, so they're memorable that this is going to get called from the API actually. So when somebody hits the login endpoint or when I hit the login endpoint, just know the API really just call this method which then made the checks and returned the response art. So we try to get the user, if the user doesn't have to exist, we throw a new exception, So we're using exceptions here later on we'll look at proper exception handling, Global Exception Handling for the API. But for now we'll just use the exceptions and we'll refine as we go along. So if the user is not found, we throw an exception, could not find that user. If the user is phone, that's fine. But then we tried to sign them in. If they could not be saved in, that's just another exception. Now if they pass all those checks, the next thing would be for us to generate their Tolkien. And that is where this private method comes in, Generate token. So I'm just going to jump down to that method so you can see exactly what this whole Tolkien generation thing is all about. So we will be returning JWT security toolkit which comes in the library for identity model Tolkien's JWT, right? So you can, as I said, include all the using statements once you have your spinning scarcely. Visual Studio prompt. Today you need reference the effects library. So we're generating the Tolkien or we're parsing in the application user that has been phoned. All right, so user was fallen here and was able to sign in. So we're passing over that user. Know, we get the claims. So claims can be stored in the database. Games, once again, are bits of information about the user. You can basically store anything as I clean the user in a little bit of information that you need for your application to run, you can add them to the user screams. That's outside of the scope of this course, but I'm just giving you an idea of what a claim really is. So it's off information about the user in the database. Let's get it rolls. We want all the rules because a user might have multiple Rosa user might be an employee and an admin. Like if I'm a supervisor than I might have to be a supervisor bottom also an employee so I can approve leave requests. Maybe what I can also make leave requests because I'm both right, So roles can have multiple roles for our user gets all the rules. So then we are now going to just create a new list of claim. And then we're going to add to the claims list all of the rules. So you see, we're just adding new claim called rules to roles are actually, there is, let me get rid of this magic string because there's actually a constant that we can use to make sure that we're getting the cart rule type Nino to avoid typos we already discussed why we don't want any, any magic strings, right? So I can add the claim types dot roll and claimed types is just a constant that has different kinds of claims that are primarily used throughout. All right, so you can always give your own name for clean, but then there are certain standard ones that you can always look for and you can just look through and see which one is relevant. But in this case, we're adding roles. So I'm going to use the claim type for roll. All right, then I'm going to build another IRI of claims where I'm seeing nucleon is equal to our new claim, is JWT registered claimed names. So you have claimed types versus JWT registered claims. So JWT registered claims pretty much. These are JWT specific claims that are generally used for for applications in general. All right. So those are direct DWT standards versus cream types, which is not necessarily JWT related, what those user related. So you can combine them because they all come down to the same claim type. So we have the roles and we're adding DWT registered clean names. Soap. Soap is short for subject. Alright? And that's generally speaking, the users, the users username or their e-mail, whatever is unique. You see GTA, which is usually just that good string, and then the email, which is self-explanatory. Then I'm adding my own claim here where I call the UID or user ID, which is the actual ID value from the database. So you see you can build your claims. You can put in as many claims, you can put in your custom names if you want. But even, even though I have a magic string here, I would suggest that if you have multiple custom claims that you need to add, don't add the magic strings, put them in another constant. Classmates is how you see the constants here and then reference them accordingly. So you would have your constant like custom clean types, dot user ID, something like that. And he WHO after we built this area, we're just going to dot union the user claims from the database and the role claims, and then everything is now in this one IRI. So moving down a bit more, what we need to do is encode our signing key. So when we get to the JWT settings, you'll see that there is a key that would be unique to your application. And there are many ways that people use to store the key in this setting. We're just going to use up settings.js, sun, some people store it as an environment variable. I did that in my ultimate API development course. So you'll see multiple ways of doing it. So all we're really doing here is signing it. And what we'll want to do also is use it to determine the sign-in credentials. All right, so between these two lines, we have the sign-in credentials. So this bit of code noise where we actually generate our security token. So JWT security token is equal to new security token issuer. And that's coming from our JWT settings audience. That's also coming from our settings and claims we just built at ourselves, expires. We're converting from date-time know where adding the minutes coming from the JWT settings. Once again. And then we're adding the sign-in credentials, which we just determined here using that signing key. And then we return that Tolkien. So once we return that Tolkien, see a lot when done in that method. After we turn that Tolkien, then we build our author response, which has the user ID, the token that we just built. And take note of the chord here, new JWT security token handler dot right Tolkien and JWT security Tolkien and the e-mail and the username, and then we return that response. So that was the response that we got from swagger with the ID, the Tolkien, the email, and the username. So now that we've done all that heavy lifting between the services and or, you know, all of that code that does jump over to the Identity Services or a distribution file where we configure the Identity Services. So like we have done in previous times, we're going to need the AI service collection and I configuration. And moving down, we'll see the different sections. So services dot configure relative to JWT settings. We're looking in the configuration to get the section called JWT settings. We are adding RDB contexts that we're familiar with that. So you could actually just copy, paste that code, just make sure you're referencing the correct DB context file. So it's leave Management Identity DB contexts, which is the one that we just created for this particular project. And we're adding SQL Server. Let me bring down the quota. You can see it's a bit more easily. So options that uses SQL Server get connection string, and this is what I'm calling the connection string leave Management Identity connection string. Now the cool thing about it is that you can actually have two entirely different data stores. If you need to have a data store for your applications separate from a data store for your users. Then this is, It's this easy to separate them because all you need is a connection string for one database and another for another. We're referencing the other connection string. So I'm actually going to be separating those datastores and you'll see that in a few minutes in the app settings. So if you wish to use the same database for both, then you just need the same connection string. No hustle. We're also specifying that the migrations assembly is in the same assembly as the DB context, which is why we have that Migrations folder. We also have services that add identity, where we're seeing that identity we're adding is application user for the identity user class and identity role. We're adding the framework, the Entity Framework store to be our DB context that was just initialized up top. And we're adding default token providers. So this adds token providers use to generate for reset passwords G and Gmail, all of those things. All right, So any if you have to do that tool, confirm, e-mail or reset password, that kind of stuff. This library, we'll facilitate those Tolkien providers for you. We are at the services and I'm adding this one as transient because we want a new auth service every time a request comes in. So it's not scoped this time. It's given me a new one every single time. And we're just binding or, or abstraction or implementation. And then we get to the services that add authentication. So here we specify options where we have options not default authentication scheme is JWT beer defaults authentication scheme and Default Challenge scheme is the same thing. All right, so that's how we tell the authentication scheme to use JWT. After that, we're adding the JWT beer options where I'm seeing the Tolkien validation parameters are as such. In previous courses where I've gone through WTI was narrower district, but once again, context determines your implementation. So for our Tolkien validation parameters, I'm going to need to validate our signing key, the issuer, the audience, the lifetime, the clock skew, which gets our sets the clock when applying a validating time. So relative to where you are, how do we apply it? Validation time, validate the EC sorry, divided issuer is found in the configuration for the JWT settings colon issuer. So this is a nice quick way to get a specific value out of our settings section in an app settings file. All right, so we're getting the issuer and the audience both from the JWT setting section of the configuration and then our issuer signing key. And we saw something like this before where we do the symmetric security keys signing for that key that is also found in the JWT settings under the name key, then we return the services. All right, not that, but now that we've done all of that in our identity, we can know, wire it up with the API. So in the API, I'm going to start off with our app settings file. So once again, we have two connection strings. I have two connection strings. You could have one if you wanted. No problem. But I have one called HR leave management DB, which were used to. I then add 14, the identity which I just appended on the square identity to it, right? So it's really not that big of a deal, depending on your business needs, you may need to separate them. A case for when you would want to separate them would be if we have multiple applications using the same user store. So in that situation then you may want a standalone database strictly for user information. And then the different databases are relative to the different apps. But all ops could subscribe to the one user store and using the same identity library that I just setup and the same connection strings settled. Every application could take advantage of it quite easily. It would be agnostic to who is actually interacting with it. So we have our connection string and later don't in the file, I have the JWT settings section. So here I just have a key. Once again, this key could be anything, it could be a word, it could be something like a password, but it's not something that you want somebody to guess easily because then people could spoof your Tolkien if they know your key. So that's why I said that sometimes people don't store it in the opposite things. They store it as an environment variable or as an Application Secret, something else. But for now, I'm just putting it here because it's our application, but you know how to store it. More security needs be our issuer. I'm just seeing each our lead management and the audience would be HR leave management user. And the duration is 60 minutes, meaning this token is valid for exactly an hour. After an hour, you will need a new token, you will need to login again. So it's your discretion that you sit this time. Some people would set it for days, depends on you and your security policy. Now let's jump over to the cones controllers in the colon controller I'm injecting the eye auth service. And you can see that it's a really simple controller. Remember, thin Gonzalez, That's our objective, right? So there's actually nothing, no logic, no serious operation happening here. We're just implementing to action results. One that returns author response on although one that returns or a distribution on response. And then pretty much we're just returning. Okay, I'll 18 the result of the login request. Once again, in our OSS service schooled, we are throwing exceptions. So later on we'll look at how we gauge those exceptions and have appropriate return messages when our client application is calling. But for now this is all we need to login and register. And then in our startup file, which is the second to last bit that we really need to pay attention to. We have a few changes accordingly. So we want you to configure our identity services. And to do this, we will need to make reference to the identity projects, right? So you will be prompted, or if, if you're not prompted, then at least add it as a project dependency when you get the chance, and then that will be available to you. So then after we have configured our identity services and other changes that I want to point out is too swagger. So I changed this to a method that is going to configure the swagger because there are few things that are going to be different. As you notice, my soccer doc was slightly different from the first few times that we used it. And this is why. So before we get to this method though, in the configure for the middlewares, you want to add up the tissues authentication and you want to double-check that you have used authorizations. So you'll want to make sure you have those two. If nothing else. Inside of this configure, anything is that you have to remain unchanged. Know the changes to the Swagger doc, and this is just the private method implemented below. So if I just collapse these two, you see the Arab League Soccer dock. It takes I serve as collection. So in the method call we're passing in the services and in this one, No, we're expanding what was just the services that had soccer, Jen. So no, we're creating a configuration on blood where we're seeing AD security definition for bearer. So this means that we're telling swagger that whenever something is authorized and we'll get to the authorization quote unquote part of the API. What, when an endpoint is authorized, then require a bearer token. That is our security schema for testing that authorize required endpoint. We just give it that little description on the some verbiage shuttle, the user, this is what you need to do. Name authorization. In the perimeter location is the header. So whatever we put in, which is a beer, goes in the header of the request. And the type is an API key, and the scheme is a mirror. All right, so this is just so slitting Swagger. Know that if an endpoint is authorized, once again, require a beer Tolkien, and that is how it should be handled. Then we add the security requirements where we see new opens. An open API Security Scheme reference API, Open API reference type is a reference type security scheme named bearer scheme off to name bear. So you'll see a lot of the things that are repeating. So you just go ahead and add that section. And then we have the same Swagger doc section where we say version one. And the type is HR, leave management, EPA and put a little space there. All right. So that's pretty much it. So if I was to authorize and buoyant, so you saw me use the API. You saw me authenticate, but you didn't see me authorize and test. So this is all you really need to let the API know that it should lock down everything in this controller. If you don't want it on the entire controller, you can put it over the specific actions. So if getting the leaf types didn't require somebody to login, then that's fine. However, if creating the leaf types required somebody to login and then you could put it directly over that post or the PUT, etc. No, we're almost done with setting up our identity services. And at this point we just need to let the database actually exist and have it updated with the relevant data, right? So I am going to in the Package Manager console and make sure that your startup project is the API and that the default project being referenced here is the identity project. All right, so the first command that you need to run is the ad migration for the new identity BB contexts. So we're going to say add dash migration. What do we have migration name is, but then I'm specifying which contexts that specification you might get an error seen that there are multiple contexts and it doesn't know which one to use. So to get our own That you just say Dutch context and the actual name of the context. Once you do that, you'll get that migration file generated. And you will have the tables being created, as well as the new data being seeded in for the roles, the users and the user role assignments. Alright? Now after you've done that, you want to do an update database. So the database is going to look similar in the sense that we have to specify which contexts we are updating. So you just say update dash database, Dutch context and put in that identity DVI contexts. And with all of that don't you would've created the database. I can jump over and show you the HR leave Management Identity DB, with all the tables that would have been created. Once again, I separated the application database from the user store. All right, so let's test this authorization and then we can end this activity once and for all. So we're going to say authorized over the get list or get list of leaf types and point. And then we will jump over to swagger and login some logging in as one of the sea that users have the Tolkien. And then when I scroll all the way down someone to test one that is not authorized just gets right. So let's look at the get by IDs so I can split it into id1 and execute. And you see I'm getting my response, no authentication required. Now, if I wanted to test the leaf types, can click that padlock. So let me just start it over. When that padlock it will give me the instructions that we stated. So it says, make sure that you enter bearer then space than your Tolkien. So this is what it actually looks like going over the wire, write the word mirror. And that Tolkien that I just copied from the response from the login. And when I click Authorize, I can note the clothes. So as far as soccer is concerned, any requests that it is going to be sending, when we click execute, it's going to include that bearer token in the header. So when I click Execute at this point, then we see our response. So let me try it again with the barista. If I click logout, it clears the bearer header and I click Execute. And I'm getting that for one, I'm not authorized, I'm not authenticated. Right? So then let me try it again. The potluck value is the word bearer. The Tolkien authorize, close, execute, and there we go. So we have secured our entire API, or at least we are putting the capability to have the EPI secured and going forward, of course now we can replicate that kind of security policy to our client application.
32. Add Authentication to Web Project: All right, So we're coming off the heels of doing some heavy modification store API in order to facilitate authentication and authorization to some extent. Now all we need to replicate those efforts in our UI where we need to be able to allow user to login. And based on their role, they should be able to see certain things are not. So here I'm logging in as the admin user and you can see I've made some modifications to the menu where I can see certain items that I could not before. So let us look what was done. Once again, I cannot do the work already and I'll walk you through it because I don't want to bore you with watching me type. So step number one, let us update our end swag generated code because we modify it or API. As a result, our documentation got modified. So we need a fresh set of code to represent the current state of the API. So once again, we'll go into in swag or runtime is done at five. We put in our URL ahead and create a local copy. And pretty much we replicate all of the settings the same namespace. We make sure that all of the, all the options are ticked, inject, generate the interface, and make sure you're up the details and generate the details and the class types. And when all of that is done, make sure that your file path is correct and then regenerates. Know, this is something we might do multiple times on a probably should have shown you this earlier, but you can actually go to file and just save this workspace so that you don't tough to start from scratch every time. All right, So I'll just save that. And when we come back, we can always just reopen it and then just save it inside the project. You can save it anywhere. That's fine. However, let us go ahead and continue with our newly generated code, which should know have the tasks or the and, or the request and response objects for our login and register operations. All right, so now that we have all of that, let us set up our local authentication services. In our contracts, we're going to have a new interface, I authentication service. And we're going to have three methods. One to authenticate, one to register 12 logos. This could easily have been called login, doesn't really matter, but this is for login, this is for distribution, and this would be forced to log out. And of course for ever contracts, there is an implementation. So in the services, I have that implementation in the form of authentication service, which is going to inherit from the base HTTP service as well as the contract. Now there's quite a bit going on in this method and I'm going to walk you through it step-by-step as usual. So firstly, I need to inject my HTTP contexts accessor. And I'm going to have a private field for JWT security token handler know some of these classes and you upload. I've actually seen this class from nor dealing with the API, you will need additional libraries. So as you are prompted to install the libraries, go ahead and do that, as well as putting in the using statements. So in our constructor, as we've seen before, I'm injecting the icons, local storage, and I HTTP contexts accessor, which is getting initialized here. We're also passing that over to the BCE or the client and the storage over to the VCE. We are initializing manuals, so this token handlers not be injected, but we're initializing it to be a new instance of JWT security token handler. Know for the implementations because of course after this inheritance it would really need the implementation. So you can go ahead and generate those methods stubs and let us know, look through together what needs to go in each. So for the authenticate, taking email password and I have everything wrapped up in a try catch. What we're trying to do is generate an auth request. So we'll get the email and the parser from the parameters. And I don't know if I've shown you this before, but in this career, in this latest version of C sharp, you can actually initialize a new object by just seeing the datatype object name is equal to new, and then you just have your values, right? So that's all I'm doing there. Then I'm seeing var authentication response is equal to await the clients call to login async passing in that authentication request. Now, if the request dot Tolkien is not equal to string dot empty server without the request object comprises a few things. It has the ID of the user, the, I think the e-mail address on other things, a few things came about what the only thing we're really interested in keeping is a token because that's all the application will have to use to see this is who the user is. So if the token is not empty, then what we want to do is get the Tolkien contents. I'm seeing var Tolkien content is equal to the token handler which was initialized up here. Read JWT, token. And then we're passing in the string. So this is actually a strong type called JWT security token. All right, then we want the claims from the Tolkien. Remember the bits of information, the email, the ID, everything that was sent, Bucky, and the token that tells us who this user is, we need all of that. So I'm just going to parse the claims from the token. Now this is a method that I have Putin and it's down here where it just returns the list of claims. So remember when we're setting up the tool can remember we compile the list of limbs and compost it and put it, encoded it and put it into Tolkien nowhere decoding it so that we know the list of claims. So in this method, we're basically saying var claims is equal to token content dot claims dot two lists. So this whole token content object allows us to just get the claims back as a list of string or list of claims rather. And then we're going to add explicit. The claim types name is Tolkien dot subjects. So maybe there was any, maybe there wasn't. I'm doing it explicitly. I'm just showing you because maybe you don't know what you're getting back into Tolkien sometimes. So you, if you want to be explicit, you can always add your own clean with your own name just like we saw before. And you put in whatever value it should have. After doing all of that, we return the list of claims. All right, so now we have the cleans. I need to create a user for my current up. So I'm seeing var users equal to claims principle and dreams principles will have a new claims Ed entity with the claims that were just taken from the Tolkien. And we're using cookie authentication. So once that person is created, we're storing that user record or that user's session, the font that there no a user currently in the system as a cookie. So we'll see how that is set up later on in the startup. But that is what we're doing. And then we're going to do a login by seeing HDTP contexts accessors. So this is our really cool library in case you're not very familiar with it, that allows you to actually access the context of the HTTP requests. So everything is happening when a user is requesting data or sending data, whatever it is they're doing, It's all happening within the context of an HTTP request. So this library actually uses direct access to that current request pipeline and allows us to manipulate HTTP contexts and it's injectable so we don't have to do it from the controls, can be from anywhere else. So I can say give me the HTTP context that we're in and go ahead and sign this user in using the cookie authentication scheme. And we're going to store that Tolkien in local storage for safekeeping. So remember that we have the low-cost storage being called by our bearer token method whenever we are going to do a call. So that is why we have to store it once you get it, and then we're just going to return true. So if the person is authenticated, we do all of that. I return true. If we return false, then we can do something else. And you'll see that in a few minutes. So that is pretty much what authenticate is doing. And if there was an error in this try-catch, then it just returns false. There was an error, right? So that's authenticate. And let's look at register, which is much simpler really. We're just going to formulate that registration requests based on all of the parameters that would've been passed in. And this could really easily have been an object, but that's fine. You can refer back to that later on when we create the registration request accordingly. And then we pass over that register Async HTTP call with that request. And then if we get MCI user ID or issue shallow successful, otherwise, it was not. Know in our logo, we're doing two things really were just clearing the storage of any Tolkien values that were there. And we are also signing off and implicitly destroying any user. B is cookies that were created during the signing EC. That's pretty much all the log belt is doing. All right, so that's it for our authentication service. Not too complicated, right? Next we need the user interface or interfaces that we'll call this soul. I created to a controller called users Controller. And here we inject I authentication service and have two options, one for login and 14 login with our post art. So login, it's just going to return a view and we can generate a view quite easily with this. So, well, let me backtrack. I'm actually using the login VM site, created a login VM. There we go, which enforces some validations are more, but I will say, you know, we can always enforce validation from a client said I don't have to necessarily rely on the API. So something like logging in. You shouldn't be able to attempt to login without the e-mail I'm parser being filled in. I also have a field called return URL. All right, so whenever you try to log in, we're going to require that you have email password and where does also validating the datatypes with those? And then in the option, we're going to generate a view. And I just said raise of you login. I use the create templates because we are going to be, we need a farm. And then we're using the login VM for that generation or so after doing all of that, we click add them, we get our simply not view that we're used to seeing in our users folder. And it's really just the login form, right? I put the return URL as a hidden. And we have a text box for our email 14 password, which I specified the type is password, so that's the ties of characters, of course. And then the button says login, know when that form is submitted, we're taking the login VM and return URL. So return URL is going to either health content or default URL contents root. And we are going to have a flag that says is logged in, which is going to be set after our weighting a call to the auth service dot authenticate where policy in the login dot email and login password. And if we get a Boolean, then we can return a redirect to the return URL. All right? Otherwise we're going to be adding a model state error to say login attempt failed these dragon and we just return the view with the login. So remember that once it caused this and this can block that comes back as true. That means you would have already created a cookie, already created that user record or that user session. And then by the time we're redirecting, the user would've been logged in. Now let's jump over to our layout and see some of the changes that we made. So in our layout, we had our leaf types, link or URL right here in this menu section. So I've removed that and I replaced it with this nice, it's a block of code. And I've also put in this partial called login partial. But let's look at a little bit of code that I've put in. So in this UL or unordered list where we have all of our nav items have put in an if statement that says if user, so user with a capital U defaults to look at the claims principles, remember that's what we just created in that authentication service, right? So if this user dot add entity is authenticated, then we want to do this. Some chicken user.name is enroll. So remember that we sent over the rule clean, right? So we can see user.email mineral, look for the role administrator. And if the user is in that rule, then we display these many items. So I just have another nav item with a class called drop-down and an anchor tag that says Manage. And I'm not going to go through every single character. I'm just going to scroll across slowly enough so you can see all of what goes into that anchor tag. Alright. And then below that we have a div that has the drop-down menu items. So you can just hit Pause and replicate that if needs be. Alright, so that's how we got that drop-down list item in the menu. At least if you're using Bootstrap 4, which comes default when you scuffled or.net file predication know in the login partial, all they did was create a new razor view, didn't use any modular and fingers and emptiers of you gave it some name underscore partial lowness where usually indicates that it's a partial anyway. And then we have this bit of code where I have another URL with class navbar. And then inside of that avenue statement, the user is authenticated. Then we want a list item that shows the user dot identity dot name. So remember that name claim that we said explicitly. That's what that was art. So that's a good, I use an intermediate speed in the navbar. So at that display right there, and then we have a logo button. So the Load button is actually a form that is going to call the action Logos in the user's controller. And I just included that return URL right there. And then that button that says logos. So if the user is authenticated, it shows that else. We want to show link for a sister and a link for a login. Hurt straightforward enough. So if they're not logged in, we want them to be able to login or register, otherwise, show them their name. So you can go ahead and replicate those lines. Although big modification that needed to be made was to our startup. So you can see here that quite a few things have happened. The last time we were here. I believe we would have had up to this point. Now we have all of this proceeding. It's right. So firstly, we have to let it know that we want to add the HTTP contexts accessor, meaning we want to allow it to be injectable into any other class, so we use it in the eye auth service. Allows us to that facility. All right, then I want to set up the cookie policy options, right? So services are configured cookie policy options are nowhere in the minimum side responsible. Sorry, minimum sight seems set policy. It seems that more than one. Alright. So once again, this is a pretty simple app. I'm not willing all with the security, you may have different security needs, our own your cookies and your policies. But at a basic level, this is what we're sitting on. And then I'm going to say services dot add authentication. And we're adding the cookie authentication defaults authentication scheme. And we just add cookie. Then I'm adding transient or adding a transient or distribution for our authentication service with its implementation, everything is, remains pretty much the same in that services configure services method. But then in our Configure, we also want to make sure that we add authentication and that we have ADD or use authorization there also. So you'll want to make sure that those two lines are prisons. And before I move forward, we also want to make sure that cookie policies there. So the three things actually, cookie policy, use authentication and use authorization. Now, one other thing that you want to do is modify your services. So know that we know that we have to have the bearer token attached to certain service calls. We can now go to a service. So you have the leaf type service already up and running. And then we can call the add bearer token method inside of each of these methods. So right before we actually meet that client call, we want to add bearer token. And just as a refresher at bearer token basically says, if the token exists, then go ahead and add it to the authorization. All right, get it at it as a beer. So remember when we're testing, it would swagger. When we had to test the secured endpoint where to go and see bearer space, the token That's all this is doing is just seeing the authentication header values beer. And we're adding that token. And authorization is the default header, right? So that's the header and it's adding bearer. This is miscalled. Putting a barrier speeds Tolkien for us. When we do that, we're adding it to that client. So by the time the client makes the call, the bearer token is present, and then everything else can flow. So you can just go ahead and add that bearer token because we don't know when something will be secured are not her. We don't know which endpoint will be secured on the API. Well, it doesn't really matter. The fact is that we know we need mirror security. And more than likely, you would own your API or an API would be locks don't completely to anybody who is not logging in are trying to register. Once you're logged in, you're expected to have your Tolkien to say, here I am these for the information. Now as a test, what we're going to do is jump back over to our leaf types controller in the API and put and authorize over the entire controller, which means that you should not be able to get to any of these endpoints with all your bearer token, you should get a 40 1, which means on authorized or unauthorized, on authenticated. Rather, if you attempt to go to any of these endpoints from the API. Alright? So remember to test, you want to make sure you have on multiple projects, multiple start-up projects, go to solution properties and let the API start. I'm starting with o debugging, say comes up faster and MVC, you can also start without debugging if you don't need to go line by line. So let's go ahead and test this. So we have our MVC application, we're going to try to login. So let me put in, but attempts. I know that user doesn't think this login and give you a few, and then it's bouncing above seeing login attempt failed. Please try again. All right. Duly noted. Let us try one that we know and then try it out again. And then this time we are now logged been so redirected us to our homepage and no ISI that Manage menu item, which gives us those options because we're in as the admin. And if I go to leave types, everything loads. Why? Because my token is present. It knows that I have access to information being requested. Oh, if I love lot. And let me try and login again as the regular user, then login is going to be okay, but there is no manage right now. What if this user was standing over the admin shoulder and saw that in order to get to the leaf types and create my own leaf type, I can do this. I'm going to try and get there. It's going to go there, right? So alter rectify this. We have a few points of attack and once again, we are options. Implement the one that is best for your situation. But from the client-side application, we should know that certain things are reserved for administrators, uncertain things are reserved for users or maybe not reserved books. Users shouldn't be able to access certain things administrators can. So in our leaf types controller, it's probably not enough to just say authorize because this means you have to be logged in. I could also see authorize with the roles administrator. So that means only administrators should be able to get into this actual part of the application. All right, so that would actually save me a lot of time on the client side trying to figure out, okay, what are the calling? War they whatever because this Authorize will actually take care of that entire situation for me. All right. So now that I have authorized only administrators to be able to access the leaf types controller. If I am logged in as a user and I attempt to go to leave types manually. So once again, it's not enough to just hide the link because if I memorize it, I can triangle. Well, you're seeing that it's seeing access denied and it's pointing to an address that doesn't exist. Well, that's fine. What it is telling us access denied, um, or, you know, it's reporting that we can't get there. However, if I was logged in as the admin, I could easily navigate there because I meet the requirements. So that is a nice way to secure your application from the client. Said now that being said, you could also secure it from the API said because the same way that you can set that authorize flood over the leaf types controller in the client up or whatever type of app you're using, whether it's an MVC blazer or angular, wherever you sit, the authentication in those you could our authorization rather, you could also set that authorization on the API. So the API developer may also have strict stipulations as to who should be able to access my controller because if it's not enforced from the upside, and then we could still get over to the API. So as API developer, you could also be very strict and see that only administrators are authorized to heat this entire endpoint, this entire behavior, right? Or suite of behaviors. So as I said, there are few options and you can always attack it from different angles. All right, so let's close out this entire activity with a few more modifications and then we're home free at least for now. All right, so in our users controller, in our app, I want to make a modification here where I check if the model Steve is valid. So if you're used MVC and all those, you cannot proceed as long as they're validation errors and not in Logan VM, we do have some validations. We want to make sure that those are in check and didn't please, this is all we enforce that validation before we even try that API call. All right, otherwise it will just add that new, that static error and return the view with the data and the errors. No problem, no for register. Let's go ahead and scuffle this one. So we just go to View, New View, razor view. We want register with our create template and our our class would be register v0 m. All right, so I'm not sure if I showed you the register, register VM, but it pretty much looks just like the registeration request. Registration, requests, firstName, lastName, e-mail, username, and password. All right, and if you wanted to put Putin up confirm password, but for now that's all we have. And we also have those attributes to make sure that they are required. So you're going to use that to generate the view. And then in the, in the post, what we're going to have is accepting that registration or is there VM? Know, I have made a modification to the register method and this has a few steps through it. One in the mapping profile of added up profile for the radius the VM and registration requests to remember or distribution on request is one of those generated models courtesy of n swag, right? So I'm mopping this the dot because I wrote it but I really didn't like it. Then once again, when you're developing, sometimes you do something one way and then later DO nursing. I could've been the center of the bit better and it'll problem with refactoring, right? So in the register method, I'm note-taking our register VM parameter and I've also updated or I authentication service to reflect that. All right, So null, it takes that whole object instead of six parameters. So that's another thing with solid principles. You don't want to pass into many parameters into a method. So when you realize that you probably exceed three or four, create an object. So in this case we only had two. I didn't want it to refactor that one, but then this one had about five or six. So I refactored it to just pass in the registration object. And then our registration request is no, just a mapping between the registration request and our registration or register VM. Right. So of course I had to inject autumn upper into this to get that up and running. And then everything else remains the same pretty much. So back to the controller. I know POS in that register call, I check is it created? And then if it is created, then we redirect. So here I'm just sitting the URL to be your content. I'm not sitting it's specifically and we redirect. Otherwise, we have a message and return the view with the data. And it would show all the validation errors accordingly. So let us also modify our Auth service at the API level because we want employees to register so we need to know that their employees, so at that point they're registering based on our quote is setup no, that would just be users. There will be no role assignment whatsoever. So we need to ensure that when they register, we know their employees. So in our Auth service, once again, in our identity project on the infrastructure, right, OT service, we need to see if the result was successful for the create user. Then we need to assign them to the rule. So we're just going to add this line after a result dot succeeded. We just say, I'll wait to use a manager add to rule async user, employee. So let us stay that registration for a spin. So I'm going to hit register and fill this out. Now one thing I wanted to point O, we do us for the email and the username. Once again, contexts are different in our context, I am using the same value for email and username. And then it would be said he could be said of viable. They're asked for both. Why not just asked for one and assign it in the bucket, which is verifiable arguments, right? So in your situation, you may need them separate, and they may need to be two different things. Thus, no problem just giving you the framework. So before I even do a proper distribution and you can see that the validation is actually actively working. I cannot proceed onto everything is in place, right? So let me put the values and then go ahead and hit register. And I was redirected here. So you have another you know, I'm always talking about the options because at this point I'm registered, but I don't know. There's no indication to see it was successful. I just went back to the homepage. Of course, if I attempt to login, I am able to login but the user doesn't know. So generally speaking, or sometimes what you would see is that they would actually log you in after a successful or distribution. So we can make a simple modification for that to happen. That's simple modification is to call or authenticate art. So we went through their distribution, we got a response if it is not empty and we would return true. Before we return true, we just want to call authenticate, which we know what it does. It actually triggers that whole login procedure, and then we return true. So then in our controller, we could easily just have some redirection to see, instead of going back to the homepage, we could actually read our two IP addresses. You will have successfully love banner, some friendly message to the user so they know exactly what has happened. All right, so that's really it for null, at least later on we have the beautification will have some modifications because even up onto the null, we are not focusing or compensating for certain arrows that would be coming back from the API. So on. We said that global error handling to take care of, but later on we will deal with that, right? No, we're just focusing on implementing these features and making everything work well together.
33. Setup Leave Allocation Management: All right guys, so now that we have or authentication setup, we have our users, or at least our users have the capability to self register and be setup as employees. And all we need to put in the feature that allows the administrators to allocate the Ds to sit employees. So that starts off with those modifying our domain objects for leave allocation where we're going to add the employee ID because we need to know to whom the number of days for this leaf type on for that period have been assigned. So we need to add that. And then following that, we do a migration. Remembering that we need to set our startup project to the API and the default project to the persistence project. And or add dash Migration statement will look like this. I named my Added Employee ID to leave allocation and the contexts would be leave management DB context. And then after that we run that update to the same context. So this is our migration file. You just do the update database and one soil that he's completed and we can move forward. The next thing that we want to do is to create a contract file or an interface in our application layer under the identity folder, I'm calling it, I use a service. So we're going to have any specialized user operations in this, I use a service that can be used by our handlers. So I'm going to have this one returning a list of type employee and it's called the get employees because we only want to allocate. These are the employees know the HR department said that all they really need for this feature is the ability to go to the page and click allocate, and then we get all the employees and allocate. The default is for the period, period being this year. Of course, we don't want to allocate anything to any body who is not in the employee role. So we need to make sure that we're getting employees. Employee is a model that was created inside off the identity folder. And all it really has is the ID, the email, the FirstName, LastName, and nothing too much, not too much coming from the database just in a forest and all who this employee might be. Thanks, we have our implementation which lives in the identity project, and it's just uses service under the services folder. And it's inheriting from the contract. And it's injecting the user manager relative to application user. And then our method for getting the list of employees were just looking in the user manager, get users enrolled async and we pass in that role. So this is guaranteeing us that we're getting anybody in the database was a user who has employee role. And then we're returning the list of type employee. So where does this is a list of application user? So we're saying select from this list into new objects of type employee. And they were just reassigning the values accordingly. And then we end it with a to-do list all in that return. So once this service is called, we're going to be using that method to get all of the employees. Then in our identity services registration method, of course we have to add it I User Service. So we just go ahead and register it accordingly. Now this is going to be followed by some changes to our I leave allocation repository. So jump back up to contracts and find our original. I'll leave allocation repository. Originally we would have written the implementation for the full current capabilities of leave allocation alongside these two custom methods. But then HR, once again, kind of change rules made it simpler for us, sono problem, but we do need some more custom methods in order to accomplish what HR had in mind. So right, no, I have a task, we're returning a Boolean that checks if the allocation exists. So we get the user ID, leaf type ID and the period. And then we have another task where I'm seeing at allocations. So you're going to see why I have at allocations, even though we already have the generic add method, the state knows that this one takes a list of leave allocation as the parameter, knowing the implementation. That you'll see what is happening here. So let's start off with the application exists. Once again task returning a Boolean, checking if it exists, we get these three parameters, so we're just returning DB context dot leave allocations that any async where we check if the employee ID matches the user ID being thin. If the leaf type ID much is one being passed in and the period much as the period. So we're just returning, yes, that allocation exists, are known for our allocations were doing something a bit differently here then with the regular ad. Because what happens is that if we're if we're allowing HER to just go and click Add allocations and there are 100 persons there. What we don't want to do is call the add function 100 times. So EF Core actually gave us an ad range method that allows us to just put in a whole list of records to be added. And it will package it in the most efficient SQL statement possible to save the changes. All right, so that's why I wrote that customized method here. So we're adding range instead of adding. All right, so now that we have our our repository is up to scratch, Let's jump over to our handler and see what changes are needed over there. All right, so in our handler had commented out these lines are just uncomment them know that we're here. So we're injecting our user service into our command or create the allocation. Know a few other changes will be needed because our create detail is asking for too much information based on what we said. The new operation or the new objective is. We'll get to that in a few. But retinal want to focus on what handler does after it is, it has validated the data that came, right. So after validating what is in the DTO, we then proceed to try and compile the list of allocations to be sent to the database. So let's see what happens here. Firstly, we get the leaf type that is being allocated, right? So we'd do I GET requests underneath that repository which was already injected courtesy off the Fatah. We needed it for the validator. Alright, so leave that repository dot GET request dot naive allocation detail the elif type ID. We get all the employees courtesy of our user service. We set the period too this year. And then we initialize a list of leave allocations. Then for each employee that is in the system, we check if the allocation exists. We just continue. Otherwise we want to add a new allocation record where the employee ID is equal to EMP dot ID and leave dy by dt is equal to the leaf type but ID and the number of days. And the period is the period right? Now, I'm not being strict on business rules. Some business rules would say that based on the point in the year, that location should be different, which is perfectly plausible. Whatever masi out to do, you apply that math against the default days and you applied. But the point is that you're allocating the days to the employee at this point. Of course, if you wanted to get that Grendel, then you'd want to do it on a one-to-one basis as opposed to a bulk operation for everybody for an entire period of a year, right? So you can play around a bit with it. But right now we're just dealing with a bulk operation. So after we have added all of those allocation records to this list, then we just await our call to the allocations in our repository, which is sending over the list and EF Core, we'll do the rest. And then we can see response is true, success is true rather, and the message is creation or allocations successful, whichever one you prefer. Know for our creates allocation DTO, we're asking for the number of these. And we were asking for the period because at least in the old system, who are able to click directly on an employee and put in the number of leaves that we're allocating money wildly. And the period that we're allocating it for, as HR said, they kind of don't need or don't want that, right? No, So that's fine. We can always just modify our DTL, scale it back a bit. And of course, that would have some ripple effects with the whole the inheritance of the allocation details. So that means we have to modify more than just the fields, right? So I would have to remove this inheritance and the note off to modify also the validator to not include all the rules relative to the the interface are it's OF to remove this whole include Andon out after rewrite the rule just for the Creates. So I just rewrote that rule where we validate the leaf type ID that is being applied for are allocated for, we make sure it's greater than 0 and we check if it exists also. Now let's jump over to our API and let it know that it should also be returning the base command response. If you haven't done that one already. Make sure you do that so that the Swagger document with the up-to-date and then two changes need to be accounted for by our new service client in the application. Because one, we change the parameters for the DTO. No, you only need one parameter. And we also need to let the server know that it should expect the base command response. So now that we've made those changes, Let's use in swag to regenerate these service client code. Just always remember that it's best to run the project. And then even if you have it saved already, because I just opened my saved version of this template. Even if you have it saved, it's best to reopen the project, recreate the local copy, and then you can regenerate your C-Sharp code. So now we jump over to our client application where we will be setting up our contract, It's implementations and of course, the user interface. So right now all we have our contract. I'll leave allocation service and it tells us there is returns response with type int. And it's going to be called creatively vocations, and it's only taking a leaf type ID. Once again, HR simplified it for us. So if you had created all VMs and so on, That's I have that would've been overkill based on the new requirements, but that's fine. So all we really need is create an IV allocations. And then in the implementation which we put in the services folder, of course, we know that we inherit from the base HTTP service and the ID of allocation service, much like what we did, the leaf types, we also inject or low-cost storage and AAE Client into this service. And then in our method, that's his creatinine leave allocations, all we have our response type, which is response relative to type int. We create our detail, which in this situation is really just the detail to see. Leaf type has that value. All right, so we know the Create, leave, create leave allocation detail is now updated to only want that value since we updated our documentation and the resulting code through and slug, we add to our bearer token and we are wheat call. All right, so I'm not even the veritable can, we can always double back and make sure that the API endpoint is protected for administrators only. And we can also do that prediction from our application side, right? So the bearer token is definitely important to remember. It's always going to be important for apec communications. So after we do that, we check if your success then we set it to true since there is no real data coming back, because there were only returning the B's response. We're not returning back an ID or something. So I don't need to set the response data to have any value really. And then we compiler errors returned response. Touching any errors. Like I said before, make sure that you have it registered in the startup dot CSS. So you just don't bullets or the startup.js. And if he had written the lines, uncommented them, then uncomment, if not, then feel free to know, put this line. Now let's make changes or user interface. So the best place that I have surmised to put this feature for a bulk allocation of leaf types is on the leaf type LR. So on that page I've just added a new form where we will have advocate with the same kind of route ID visa guys to see him looking formula to delete. So here we're repeating the form. So as I mentioned, sometimes beep, instead of having multiple forms, would have one and then use JavaScript to trigger the one form. But that's okay in this situation, all I want to know is that it works right? So we have the ASB actual and allocate. It takes the same route ID and we have a button that says allocate. And this one is just saying, Are you sure you want to allocate to all employees? That is the prompt that you would get. Now we have a post method in our leaf types controller called Hello Kate. So I just put that one right underneath the delete, right? So in our case, we are trying to get the response from leave allocation service. So subpoint told that we have to inject that into our controller. All right, So we tried to get the response. I just use Control M and O, just note to kind of collapse all code so you can use that for B code files. So our allocate method is going to call allocation service create altercations, passing in that ID. And then if it will successfully redirect to action to the index. And well, I'm just going to return bad request here just in case it was not successful. So we know it wasn't her. Later on you can add some jQuery or something to handle the return type in a better way, but right, no, we're not prioritizing that. We just want to make sure that we are putting all the things and the foundation in place, right? So let us actually take that one for a spin. Let us try and allocate. Remember to put on multiple start-up projects. And having logged in as the admin user, we navigate to our leaf types and then we try to allocate. So yet our prompt Are you sure we click Okay, and we're getting an error. We are getting an error, HTTP Error 400. So this is indicating that bad request. All right, So something definitely went wrong. So after tracing and tracking on that error, it landed right here in the validator. So what happens is that when we're creating, it is not coming across as valid that create leave allocation V2 is not being valid. That is because it is seeing that a leaf type does not exist. Now we know that's impossible because we just clicked on the leaf type. It's in the same line. We know that it exists and the arrow is right here. So that is my, uh, but so i unit tests probably would have caught that we ever written in unittest. So we have to spend time tracking these things don't. But the point is that We should be returning if it exists, because we're seeing if it exists or true. So it must exist. So this must be true. So I shouldn't be returning, returning not true or not aerate. So that's an error on my part. So because of that error, actually will have to go to every validator just to be wholesome. So make sure that none of those validators are making that same mistake. Now the good thing is that we kind of created central ones. So we don't have many, many changes to me. So those take off that not sign off the leaf type exists, check and we should be good to go. All right, so let's try that one again. Let us try to do the allocations. All right, so we're back and then let us try to allocate again. We get to our prompt, it does its thing, and the page refreshes so there are no indicators, right? We could of course, quoting some messages to say, you know, this action will call those completed successfully. But the fact that it really refreshed the page according to our code. If it is successful, then redirect to the index of the current beach, which is good. It was unsuccessful so into butter request. So at least we know that our allocation is working. Let us check in the database to see what our locations have been created. And we see here that we have allocations for leaf type one for the period of 2021 and the employee to whom it is assigned. So we have three employees in the system or IL-3 employees in my system, we're probably just seeing one or two if you have the seeded user and he created a user since. But the point is that we have as many employees are in the system, you'd have those allocations. So if I do that again with security, which is id2, click, I locate, click Okay, and does its thing. If I refresh this data, now we're going to see allocations for sick leave for all the employees. So div allocation is not working. That's what HR artwork, that's what they get. So later on you see the of the foundational to extend this, if you wanted to give a dedicated screen, you'd have to go fish the employee, allow them to edit the allocation of money valley. And then, you know, to set up your handler and your API endpoint to be specific to that. We're not going to get that detailed right now. We just want to get the base functions done. And so far we're finished with leave allocation.
34. Setup Leave Request Management - Part 1 - Employee: All right guys, so we're winding though Nestle and we're moving on to the next module, which will be our leave requests module. So our journey here starts with a modification to a leave request domain object where we're adding requesting employee ID, alright, because whoever's logged in is the one who is going to be making the request. So we need to be able to say that this request came from this user. As usual, following a modification to our domain objects, we have our migration command and then we have our update database command. So you can go ahead and do those two. And once you have successfully completed that, we can move on to our next activity. Let us review our Create leave requests details. So we said that the Quest D2 needs to have the date would start and end dates, the leaf type being applied for, as well as requests comments. Notice there is nothing terrible the user ID, right? So you're probably wondering, okay, so how do we know which user is logged in at a time? So remember that we are using Tolkien authentication, which means that between the MVC up on the API, we can access different bits of information from the Tolkien. One bit of information in the token would be that user's ID. However, I don't want to access it from either of these places because I don't need to access it until I'm really creating leave request. And we have the handler here that is taken, that command requests or commands object. And then it is going ahead and validating it and then creating that the request if it is valid, and then passing it the database. So that means in-between these two steps, we need to make sure that we put in that new employee ID data. So remember that we had used the context accessor buck when we were doing the authentication in the MVC application. The cool thing about it is that we are able to use that context accessor all the way over here in our application layer, in our commands. So we're going to access the HTTP context of the API from inside the handler. That way we don't have to access it until it's absolutely necessary. So in the API startup file, I'm going to add the services.js HTTP contexts accessor writable, the swat Swagger doc method call. This, then enables me to inject into the command my HTTP contexts accessor. And I can go ahead and initialize the field, but I do need a library for this, and that is the Microsoft dot ASP NET core abstraction. So go ahead and install the latest version. I'm using NuGet, rename our field according to our naming convention. And then inside of the handler, I can easily save var user ID is equal to my HTTP context accessor dot HTTP contexts, dot user dot claims first-order default. So it's quite a mouthful, but let's just assess what's going on here. Up until this point, we have gotten the claims principle. That claims principle is the same principle that we're able to build in the tenant application by virtue of the Tolkien, alright, so because the token is coming over from our client to the API, we're able to access the token information from the handler courtesy of the API. And then we can access that User, look inside of the list of claims and find the first floor default that has the same name type key. However, you want to assess that what a type here corresponds with the claimed name that we would have given it for user ID. So that's another reason I would suggest not using the magic strings because one spelling error here could throw out the entire thing. What we can do that in the clean-up activity later on. But right now we're getting that claim UID, which we sit manually. And then I'm just using the default or four, if it's small, we don't get another exception, but I'm really getting the value. All right, so that's how I get the user ID. Now that I have this user ID, later on I can see leave request dot requesting employee ID is user ID. Now I did it all the way up here because I may want to use this user ID and another operation later on. So I'm just keeping it here. And another thing that we might be interested in is the email address. So don't here we are getting the email in a slightly different way. We're seeing email address is equal to its GTP contexts accessor, all of that user.name find first. And then we're looking for the exact e-mail claim by its type. Since it does a different technique to what we did up here. And we're getting that value. So you can take multiple approaches to get this. These bits of information because he could have just gotten the claims principle in one sweep and then use the find first law, the first law default afterwards to get whichever bit of information you want. No problem, but at least you know or know how to get the e-mail address so we can know say to that e-mail address and dispatch our email at the appropriate time. Now before we move on from this particular handler there other housekeeping up operations I would want to carry out before we allow a user to proceed with a leave request. And so let's think about what exactly needs to happen during this process. On a user needs to state that they want to have a certain leaf type during a certain period. They can add additional comments if they wish. And then they submit that. They can only get that request if they have the allocation. So that means we're not even going to put it in the system. If what they've requested exceeds their current allocation. If they request it and their allocation is present, then that's fine. Later on, when an admin approves this, then we go ahead and do some deductions to make sure that when they if they requested five days, then the 5D is less in their allocation. So those are the things that we want to be keen on. Now this is a, this is the most complicated of the modules. So there's quite a bit of work to do here. So let us start with the checking for the allocation, because if they don't have the allocation, then they can't have a valid operation going forward, right? So let's go ahead and inject or I leave allocation repository into the system. And then what we want to do is be able to look up the allocation for this particular employee, which is a method that we do not have R. So let us jump over to that story and implement that. So we're creating a task that returns leave allocation, I'm calling getUser allocations and is taking that string user ID as well as leave ID, right, so we're going to jump over here and go ahead and implement the interface in the implementation repository class. And then what we're going to do here is do a lookup and return our DB context looking leave allocations, finding the first-order default where the employee ID matches the user ID and the leaf type ID matches the leaf. No bucking the handler. I can go ahead and get that allocation by calling leave allocation repository Git user allocations, passing the user ID and that request dot leave, request detail dot leaf type ID. I'm now going to calculate the number of days requested. So I'm just doing a quick Caucasian between the end date and the start date to get the total number of days. And I'm making sure that I get it but as an integer. And then I'm going to say if the number of days requested is greater than the number of days in your allocation record. I'm going to add this validation error. So this corporation could have happened here, right as we're seeing, I'm doing it handler and I'm using the fluent validation result to just add that because in case it's not valid, I want to make sure that this validation is also present. And so when it's returned with the errors that will also be there. No, I'm doing this in the handler, but I could have easily done this in the validator also. But then of course I would have had to inject all of this and create a custom validation on the NDA to make sure that I do that calculation and everything. So I'm just giving you the options based on your situation. You may want to keep all of those things inside of the validators. You may not even be using validator. So you could be doing all of this validation right here. It depends on how you want to lay your application code out. So now we can just go ahead and clean up anything else in this handler, we may have to revisit data, but at least we're looking at how we expand our handler and start putting some other things in their life HTTP, context, handler it, access or other, sorry. So when we move on, you can clean up any little messages along the way and make sure that your code can compile. So just a quick build. Now let's fast-forward to our app. We want to get the feature in where an employee can request leave first and foremost before we put in any other administrative features and anything, right? So I want us to make sure that we have that view model. I would have showcased it before, but leave requests VM and upload all the view models here. And these view models are really coming from the existing code largely. So the Create leave requests, VM, and that takes the start date, the end date, the time, the leaf ID, and some area for comments. No, I would jump over to the contract for the I leave requests service where we have a few methods that we're not implementing all of them just yet, we're dealing with a credit so you can go ahead and put them all in. And create the matching service implementation and allow it to implement them all. But in focusing on the create method, what we have is similar-looking core to what we did with the leaf types. However, you would notice that I have a red line underneath the part where I'm asking for a response. That's because as far as AI service, client or, or insert code is concerned, we're still only looking for a task. That is because in the API, I need to do two things. I need to let the documentation or that on post, it should take the base command response. Remember that we had outfitted all our handlers to return this. And this is essential because when we use n swag to regenerate or code, which I'm about to do, then it will know that it should expect this response type. Once again, create the most recent local copy, go ahead and generate and know that that is done when I jump back over to my code, there are no red lines because now it knows that it is returning the base command response. So everything is good. All right, so that is basically what we're doing in creating the leaf request. So let us now focus on setting up the controller and the supporting code. I went ahead and created a controller with read-write operations, you know, to do that, No. And I injected the leaf type service and leave requests service. So you're probably wondering, okay, why do I need the leaf type and leave requests service if I'm only creating leave requests, well, the thing is that the view which I have already created needs to have a drop-down list for the leaf types because when somebody comes to request leave, it's based on your flow. But the way we're building it, they come to request, leave that off to select from a drop down list which leave type they are selecting for, the start date, the end date. And then they're able to put in their comments before they go ahead and select request. Alright, so that is basically what I'm doing and this is the view coming from the old system. So literally I've copied and pasted this view from the old system. The only update was that we change the model to be the new model as opposed to whatever new species there. But I'm just showing you that it's the same class. So you can always go and get the code file and just copy and paste. And you should be up to scratch as amounts of alpha. That's why this is here. I'm not using any date pickers. I'm not complicating the interface just yet. So I'm removing that. Did Becker friends Our just using input type beads depending on the Default Date Picker given to us by the browser. So Buck in the controller, we need to prepare that drop-down list before the interface load. So instead of four, get Create method, what I have is a call to the leaf type service to get all the leaf types, store them in this variable. And then I have leaf type items where I'm creating a new select list with leaf types and using id and name as the key and the text fields. And then I'm creating the new model where I'm passing in leaf types as leaf type item. So remembering creates leave requests. We had that public eye innumerable select this item property, and this could easily have been select list. Alright? So whichever one works, but lets us work with select this because it's actually easier to type on work with. So that is all that's happening here. And then we return the view showing the model. Knowing the post method, we pretty much follow the same procedure if the model state is valid. Because remember that in our view model we have a few validation rule, so we want to make sure everything is met before we can proceed. As a matter of fact, I'm just realizing that I'm missing a validation rule over the leaf type ID itself. So they must select a leaf type ID before they can proceed. Alright, so let me just space them all so you can see everything. There we go. So if the model state is valid, then go ahead and width on the response from the leave request service work, which is going to attempt to create a leave request. And then if it is successful, then we can redirect to index. There is no index page here just yet, but that's no problem. And then we can add any model, model state arrows coming from our response validation errors. But then if the page has to reload, what we need to do is actually reload the select list of leaf types, right? So one thing with drop-down lists in case you're not so familiar with them. Anytime you're loading the page, you have to load up that list. So we loaded the page first tier, where to load up that list, then return the model. We have to do the same thing here. What do we already have the data coming in from the model? So we just need to reassign the data to the newly loaded select list before we return the pH alongside any errors that need to be displayed. Now, I bet authorize on the speech that's pretty standard at this point. And in our layout file we have a few changes. So I have put the links there only for employees to be able to go and create our view. My leaf, I haven't done my leave just yet, but here are our links, right? So in the same That's it's checking if the user is authenticated. I'm seeing if it is an employee who is logged in because administrators don't have allocations, so they don't need to go and requests leave, right? So if it is an employee who is logged in, then they can see two new NovaLink swan going to requests and create. And it says requests leave on, although I'm going to leave, which we haven't done yet, but we're getting there slowly but surely, right? So after doing all of that, let us go ahead and set or multiple starter projects once again and test this out. So now I'm going to login as the user admin, but the employee. And once I do that, I see my two links in the navbar. So when I go to requests leave, I am getting this error. It is a 403 error and I see it and I know exactly why. So I have mentioned that we are yet to compensate for all the exceptions that might be thrown via the API. This is one of them. This is a 43 which is an Unauthorized exception. The problem is that back when we were testing hotel lot donor end-points, we had set authorized administrator on the entire leaf types controllers. So know this employee needs to have the list of leaf types display to him. The application is making that request on this endpoint, but he's not an administrator. So what we can do here is t the author is off or at least the roles stipulation off of the authorized and only authorize those that are augmenting the data so that the post PUT and delete those adjustments made. We can try this operation again. And voila, so no, we can safely navigate there as an employee. So let us try to apply for vacation leaves. So I believe that this heading, you can see that it's going all the way back to January, I guess the start of time, we can always set defaults because you probably don't want that expanse of time. But we're not going to do that. Farnell, what I will do is simply select the first of January to the 5th of January and notice it's month, the year. That is the format it's using. All of that is customizable, but we won't get into all of the original. So vacation leave. And then when I requests leave, I am getting another error. So this error, it's saying that parameter cannot be null. So I suspect that this is an arrow coming back from our API that is not being handled once again, but let us go into debugging mode and see exactly what the problem is. And here is the culprit. The cool part is on us trying to get the e-mail address. So let me just backtrack a bit. We do have the user, we have the claims. And if you look in there, we do have the e-mail address claim. All right. So the email address is present. However, I believe I'm using the wrong claim value here. Say if I go back to where generated the JWT, which is in the auth service. I did use the same claim name which was simply email. Right. So if I right-click or control-click, you'll see that it's just a static text email. However, in contrast, it is actually seeing that clean with a different key. And that key is more fitting off the XML soap schema. And that claim type would actually, or that text would actually be present in clean types. So let me stop and go ahead and change that to clean types thought email. So it failed on the email address retrieval and that is why we didn't. Well, you've got that error, right? So if we check our leave requests table, I'm pretty sure that we are going to actually see some data, right? So you see here we do have vacation leave and it's therefore every time that it was purchased, right? So it is indeed working. It was just failing on the retrieval of the email address. So with that fix, we should be able to proceed on to unhampered. But then you do see that something completely unrelated to the key operation caused the failure, which is kind of why we tried this whole try catch to just send the email without bothering everything else. Well then we see that this is also a failure point. Granted, we just fixed it. Well, we could theoretically take this and also place it inside of the try catch so that anything e-mail related doesn't throw an error. Now that we have the request process, don't let us take a break and when we come back, we'll look at their approval process.
35. Setup Leave Request Management - Part 2 - Admin: Now that we have our leave request on the employee said, Don't. We need to actually put in some other things so that the administrator can actually see the pending requests and approve or reject. Now, this comes with some breaking changes to solve the handlers and some other requests, some of the details, quite a few things that we have done up to this point, but as usual, I will just walk you through all the modifications needed somewhere to start off with the fat that's actually went ahead and created that cost constants class for the claim types are discussed. Removing the magic strings like putting in UID many-body, right? So I created a custom claims types, just a public static class. It's in constants under the application project. And we have public cons String UID equals UID. So this now allows us in the auth service to say costume phenotypes dot UID and pass that in and anywhere else that we may need to reference this coastal plain type like we did in the other handlers. Then we can just go ahead and say custom claim types dot UID. Know another modification that I'm making is to our user service. I had one method getting all employees now have another one to get only an employee based on the user ID. All right, so in the implementation of that method, basically I'm just saying Get me the employee find by ID and then I'm returning a new employee object with the various fields included. Know I have also updated our leave requests list, DTO, and I have added the start time start dates, and indeed, those weren't there before, as well as the employee object and the requesting employee ID. So know when the admin is viewing the list of requests, they can see what the requests, the start dates and end dates of cetera request, as well as the details of the person who has requested it, would have made a similar modification to the leave requests where we have made sure to include employee information. Now, a quick modification to the mapper and at this point is optional because I want you to think critically, not just do as I do, but think about it. We have a field called data requested in the leave requests domain objects. So that was created as it was modeled off the previous system. Now remember that we are refining the system and getting rid of some of the redundancies and quote snails. And I think this is one of those quotes runs because the date requests that really is the date created. So we are already capturing the date. Every record is created. I don't have to put in the requested again, and frankly, we didn't do that in a handler when the request went in. So what I'm doing here though is I am actually doing a mapping, our custom mapping for our certain fields. So I'm seeing that whenever you're mapping from the domain object to the DTO, look in the detail or the destination, and get the date requested field. And then I want that to be mapped directly to whatever is in the creative field and leave request domain object. All right, so once again, that is optional. What sincerity, how the fees I'm just showing you the power of autumn upper helps us to just grown, make sure that all the data is there, it's relevant and appropriate place. Now, moving on to our handlers. So I've made some changes in the leave requests handler and leave allocation and loss for both the list and the detail. So I'll start off with the list since that one I think has more changes than not. So one, I've modified the request to have this flag to say is logged in user, meaning, are we trying to get the leave requests list for the logged in user are for an admin. So in the handler, I'm injecting the HTTP contexts accessor as well as I use it. Don't think those are there before. Know some injecting them and anything else that wasn't there before. No, feel free to go ahead and replicate. But then inside of the handler, what I am doing differently, no, I am initializing the leave requests this top, top. And I have another list for the detail that will be returned, which in this case is leave requests list PTO. Then I'm checking if the request says it is for the logged in user or not, right? So if it is for the logged in user, I want to go ahead and get that user ID there. I'm using my constant, see how nice and clean that looks. All right, so I'm getting the user ID and then I have actually implemented another method to get leave requests with details relative to the user ID. So let's go to that method implementation. So in the contract for I leave requests that I have to get leave requests with details, the original one that we wrote together and this new one with the string user ID. So that one with the user ID basically just adds on a condition where the requesting ID on the record is equal to the user ID being passed in. We still include the leaf type details and send back that list. Now after doing all of that, I used the user service to get the employee relative to the user ID that is, there. And then I do a mapping from the domain object into our detail. Then for each request object in the list of requests or details, we're assigning that employees value for it. So once again, this would be if I am logged in as a user and I wanted to see my list of requests. This is going to handle that. Now if the admin requests it, it's pretty much the same as it was before, where we just get all of them with the details. And then for each request, we're going to actually look up the employee on the fly, right? So whereas we know the employee because I am the logged in user, so it's only one of me. We don't know there are many different employees. So for each request, we're going to fetch that employee and put it inside of that. And at the end of the day, we return the requests. Likewise, I'm doing a similar operation for the leave allocations, where I am fetching all allocations from the database have a similar method and pretty much this code looks the same way. All right, so just the same way that we add the naive allocation leave request with details relative to user ID. It's the same way implemented method like that for or leave allocations where the employee ID is the user ID and we are the same if statement. We have added the same kind of flood to our request, right? So request us who is logged in user and then we have the if statement. If it's logged in, then pretty much the same procedure as we just saw. Relative to leave allocations. However, then I have get leave requests detail. So that's when you want one leave requests. So picture it. When the admin sees the list of leave requests, he or she needs to click on that leave request to then go to a screen to see is it pending, is it's approved or be able to approve hundreds or rejected at that time. Right. So I have modified the get requests with detail where I have put in this line to include the employee information. So pretty much all the modification so far are just thrown in the fire that we need to put in that employee information. So since we've updated that request class, we need to know, let our endpoint or behavior reflects this new parameter, right? So on the get, I'm going to pass in a Boolean. Is it the logged in user that we're after? If it is what I'm defaulting into falls, right? So that's how you can put in a default parameter where if you don't provide a value, then it's false, right? So regardless of its value, we'll pass it into the request object and then the handler will meet the decision like we just saw. So I've done this in both the leave allocations and the leave requests handler gets. As usual, once you update your EPA, anything with the blue turned to have anything in the controller, you want to rerun and swag and get a fresh copy of that client code. Not want to jump back over to our client application. We're going to look at our contrast. So the next few ones that I want to absolutely pay attention to would be the approve the request and to get the admin leave requests list. Alright, and we also need to get leave requests by ID. So at least those three need to be implemented right now. So I have other methods there, and by the end of this course they will be implemented. But I'm not going to spend too much time window on the rabbit hole of trying to get every single feature in. For this module, we're just going to focus on the employee being able to ask for leave and the admin being able to approve or reject it. So after you have included those three methods in the contract, we go over to the implementation. So of course by now I'm sure you have updated your client code. So everything that I have here should work for you. So for the approved leave request, what we're doing is taking int id and the flag or the Boolean to see is it approved Yes or no. Then we are going to formulate or requests where we put up change leave request approval detail, and we pass in that approved and the ID. And then we await the client calling change approval async, where it sends over the id and the request. Our gets leave request is fairly straight forward. It's just returning leave requests view model. We add our bearer token ofcourse, which we also did in the approve the request. And then we go ahead and try and Fitch from the get-go leave requests, get a sync with my ID and return the mopped version of leave request in the form of the leaf request ViewModel. We'll look at that in a few once again. Next noteworthy method would be the get admin leave requests per list. Now I'm going to explain what I'm going for is in the original application on what we had done was we got all the leave requests and we tried to group them and then. Figure out is it for the total leave requests, is it for how many are approved pending, et cetera, et cetera? That's exactly what I'm doing here. So I'm getting all the leave requests and notes this time I have that parameter is logged in user colon false. I'm just naming the perimeter. This part is actually optional, but I find it useful. So that's I can't remember what the value really means in terms of the function call. But I'm passing in falls because this is not the logged in user. This is the admin, right? So the admin is getting all leave requests and then I'm just creating that model admin leave requests view model. And we're passing in the total number of requests, how many are approved, rejected, and putting in the list of leave requests in general, and we're returning the model. All right, So once again, all are heavy lifting happens inside the service? No, inside our mappings configurations, I have updated that to reflect some of the new mappings that we can expect. So for a leave request detail to leave request VM, there is a slight difference in the datatype between the deeds. All right, So on one side we have datetime, but then the detail that gets generated via or in swag code actually gives us a datetime offset. I'm not entirely sure why it does that, but the solution would be that when we are mapping, we just say four member neat requested, go ahead and map it from the requested dot-dot-dot time. And we do that for the start date, and we do that for the end date. And that actually will take care of that error. So if you fail to do this, if you want to test it, you can comment these out. Attempt to run the code that would require mapping. And then you'd see that autumn upper error saying that the mapping is failing for the Beta related fields. All right, so you can do that for the leave requests to leave requests, VM, leave requests list detail to the leave requests VM. And then in addition to those new ones, I also have one here for the employee where I also have created the employee VM, which looks just like the employee ETO know inside the leave requests view model file. Let's just review some of the view models, at least the ones that are absolutely necessary for us to complete this activity. So we have added the employee VM with employee inside of this view models, of course, ultimately we'll see any two properties with the same name. It will try to map them, right? So it's implied that the employee will map to employee from the detail to the ViewModel, right? We also have the admin request ViewModel which we didn't necessarily look before, no. So that's as these fields and total requests, approved requests, pending requests, rejected requests, and the list of leave requests. So you can go ahead and make sure you have representation for the ViewModel in your code. But going back to our leave requests controller is C. You know that I have three new actions are 14, the index, one for the details and one to approve requests. So index that will take model and this model will be of type admin leave Requests View VM or what is he calling the leave request service dot get, admin lever, quest list are the administrator will see this page where once again those statistics and the list of outstanding requests will be on display. Our details method basically will be there for when they're seeing the list and they click on one of the items. And then we want to go and fetch that leave requests with all its details and return the view accordingly. Then we have the Approve Requests, not approve requests will basically see go ahead and approve the request, it HTTP post. So we're going to put a farm on that page where we're going to call that passing that leave request ID as well as if it is approved or not. So if they're approving or rejecting whichever one, it will pass that in. And then you're seeing how it goes over to the API, to the handler and what takes place before we implement those views. However, I just want us to follow up the full extent to what happens when it is approved. So I'm going to jump over to the updates, to the request handler which has undergone some amount of changes also. And I'll walk you right through it. It's, of course you pause and replicate as you need to. So we're know injecting the leave Qi Shun repository in addition to anything else that was being injected into this file. And the reason for that is we need to be able to deduct the allocation when there is an approval. So what we're doing here is refactoring our handled method for this. So I get the leave requests firstly, and then I see if we're dealing with a leave request detail than I want to run the validation because what was happening initially or the way we wrote the code initially, the validation code was being run regardless of this being null or not. Know if the validation test to validate our null, then that's also going to throw an error. And throw everything out. So we're just validating our leave request DTO when it is present. And then if everything is okay, we go ahead and do our mapping and our updates. Now. Otherwise, if it is the change dv requests detail that is not null, which in this case of approving it is then isn't rather than awaits the leave requests repository change approval status method passing in the request. And the fat out we wanted our proved. Then I'm going to say if the request dot change approval detail, that's approved, dot value, if that is true, right? It's all of this really evaluates equivalent to true. But of course, when dealing with Booleans, you don't necessarily have to see equivalent to true. We can just see if the boolean, right? And we don't have to worry about another exception because it always should be, it always should have a value. So that's another object we can make toward detail because right now I think we made it nullable, which no, we think about it is wrong. It should always have a value so much I'm going to remove that Boolean and we can update our swag client code accordingly, but later on we can do that. So I'm updating that detail to always be either true or false. So if it is true, then we get the allocation for the employee. So var allocation is equal to get users applications. This is a new method. So this new method, once again, you put it inside of the contract. But when you implement it, it's basically taking the string using the user ID and the leaf type ID. So we go and we say, get me the leaf allocation. First, our default async, where the employee ID much is the user ID being passed in and the leaf type ID matches the one being passed in. So after doing all of that, we pass in the requesting employee alongside the leave requests. So we've got the leap request here. We have the employee who requested it and we know the leaf type courtesy of the request once again. And then we're going to say, get me the number of b's, right? So we already saw that in the creation are in the request rather, how many D's are being applied for. In this situation, you could make another decision. You could decide to store the number of Ds in the database from the get-go or calculated on the fly like we're doing, no. So that's up to you. Either way we get the number of beers, we go ahead and adopt the number of these from the d is requested from the number of days for the allocation. And then we run an update for that allocation, then return. No, in terms of our views that much our controllers, we have created the index and we have created the details. Truth be told, the index is a split copy of what is coming from the old code. So if you have downloaded the whole source code or you can just fine, It's on GitHub. You can actually take everything out of that matching leave requests index page and please here, I haven't modified anything. Any modifications that are needed would be due to naming differences. For instance, here I have item.name employee instead of item.name requesting employee. Outside of that, though, the code is fairly the same. Now, for the details, I have made some modifications because in the original code, we had repeated this alert section on thrice. And what we did was we said, if approved, then show this alert code with a different class name at different hidden text. Else if it's true, then show it again with right. So instead of repeating this and that's another part, dry, don't repeat yourself. So enter factor, you always want to say, well, something needs to be repeated at some point, right? Or some operation needs to be redone. But which one can I redo with the least path of resistance R, which would be easiest to maintain in the long run. So my refactoring of what was done in the original details pH would be that I am creating two variables here. One interesting, one for heading, heading text. And then if the approval is null, then I give className and hitting takes their values. If it is true, I give them different values, et cetera, for. And then I load the div with the other at one time where I dynamically pass in that class name and heading texts, because everything else inside of that alert is going to be the same regardless, right? So I say employee name and then I put in Model lot employee first name and last name. The date requested is that they requested coming back from the model. And then pretty much everything else is the same up until the last part where we are checking if it is approved or not. So instead of having to link buttons as the original code had ever up them in farms where they discussed why forums are far more secure. So for method post is the axon prove request. And in this one I have the ID which is a hidden, and it has a value for the modulus idea also have another hidden that has the name approved and the value is true. I repeat that feed for the reject, except the value for the approved is false. So when they click either of these buttons is going to call approve requests, which is then going to get ID and approved costs that don't we just went through what the handler does with all of that information. All right, so we've got this module for quite some time, and this is usually the most difficult and time-consuming module. I've tried to reduce the time as much as possible, but then there's much more to do, but at least you have the foundational concepts so you can take it and carry on. So let us just preview what this leave requests will look like from the admin point of view. And weekly cleave requests will see the list of leave requests. So I've already approved and rejected one and that's what that code looks like. And this one is pending approval and we see the start date, the end date, the vacation leave type, the employee name, everything is nice and, you know, spit out for us. So if I click review, we go over to the page where it says pending approval they requested and then I can approve or reject. So if I click Approve, then it will redirect assuming that there are no errors. And actually there is an error. All right, So the problem here is that I'm using the code from the create in order to do the calculation, which is completely incorrect because I should be using the lever request object and not request dot leave requests detail, right, so let me just update that and we can retry that operation. All right, so let's try again. I'm going to click Approve. And there we see it's not approved. Know the problem that we are going to have to address later on is the fact that we have multiple operations being carried out against multiple tables. But guess what happened? A participant and the other part didn't, meaning this was approved, but the deduction did not happen because we had a failure on that lake. Right. So we actually successfully did the approval, but then the deduction part did not go through, so it did not get updated. And what we would want to do is to do something like our role buck, it should be all or nothing. So if one part fields then everything should feel. So that is where the unit of work kind of comes into play or at the database level we call it transactions. So later on we'll look at all of those housekeeping issues with our application. Whenever an application this big, you have to look out for those things.
36. Unit Of Work For Batch Operations: In this lesson, we are going to be setting up a global exception handling middleware for our API. So what happens is that we had custom exceptions that we created from almost the start of the project. We have been throwing exceptions at certain points in our handlers. However, we have not necessarily told the API whole it should respond when exceptions are thrown. So of course, you'll want to always gracefully fail if there is a type of exception, we want to send back a code that is indicative of the type of exception. For instance, I have updated all my update handlers to also throw a not found exception. So upon to know, let me just correct this one. So open to know. I didn't have the code. If you already had the code, then that's good. That's kudos to you, right? But we were throwing validation exceptions when the validation fails. But then what happens if the record to be found was not phone, then we want a not found exception. So I've added that on each check to see if leave allocation is found, if the leave requests, That's a boat to be updated his phone and if the leave sorry, the leaf type, their goal. If the leaf type is phone, we just throw a not found exception. Alright? So throwing the exception is easy enough. Handling it is another thing. So notice that there are no try catches on. It would be kind of overbearing forward to try and put the try catch and every single one. So what we're going to do is set up and exception handling middleware at the API level because the controller uses media to call it a handler, but we don't have any track catches here either, so we're always returning, okay? But then there are times when the exception is thrown and it Okay, can be thrown. And in the API is literally just throwing some random response buck on the client. We want to make sure that we know what is being thrown. So go ahead and create a new folder in the API project called middleware. And in that folder create a file called exception middleware. So that's our cross exception middleware. And I'm just going to walk you through what this is going to be doing on web or details. So we have a class in that file, in that other class called error details. I'm just walking you through the simpler parts first. And error details just has the error type and an error message. So at least we can always inform the client this is what went wrong based on the circumstance that we're faced with right? Now. All it said that we have inside of the class for exceptional middleware, we have private read-only field called next, and that is of type request delegate. So we instantiate that in the constructor. And then we have a method called invoke a sink. So public AsyncTask invoke async and it takes a parameter called HTTP contexts. So we've already kind of looked at what the HTTP contexts allows us to do. Basically, it allows us to see the request, the response, everything with a whole workflow between client and server is stored inside of this HTTP contexts. So this is going to be acting like an interceptor, is going to try to see. It's going to say do the next option that should be completed via the HTTP contexts. That's basically what that is doing. Her. Http contexts do your next option. If there's an exception, let's catch it and then we will handle it. Now, let's go on to how we handle it. So private task handle exception is sink where giving you the HTTP context and the exception that was caught. Know we we are just saying that we want a response punted to be application slash JSON because it's the API. So we know that everything that we respond is going to be in the form of JSON. We're also setting a default internal server error, HTTP status code status school is defaulting to that which is 500. Then we're going to say the result is equal to, I never want to serialize objects into a new instance of arrow with the exception message. All right, so we're serializing all of this into the results null when we go on to the switch. Where basically seeing what type of exception is this, because exception is the base datatype. But then as we've seen, we have our own exceptions. We have the validation exception, we have the let me go out the butter requests exception. We have the not found exception. So we can account for all of these accounting for bad request. I'm not accounting for validation. Me. Go ahead and update that. So what we're doing here now is seeing tell me what kind of exception it is. I know it's an exception, but which type was it really wasn't that bad request. An exception of validation rather owes its I'm not found. Based on the one dot to do is we're going to change the status quo. So it's defaulting to 50, 100. Meaning if it was just the unexceptional, maybe it was at database level failure, maybe it was a network failure that we can't buckle. And four, then it's definitely a 500. 500 means it's a system, the system that the API is using, it's the system's fault. However, bad requests would indicate that you are at fault as a client, but you sent me garbage data. So I'm going to tell you it's a bad request, which is a 400 year ago. If it's a validation exception, that's also kind of a bad request because you sent me but data, but you can always look through and see what other code might or better for the type of exception. All right, but then you always want to stay in the 400 range with error codes, right? So that's where we are. So I mean to you That's alone. So this one says But request and then not found exception is a 40 for meaning I couldn't find what you're looking for. So 40 for so if none of these was the case, then we just break and that's would remain as a 500. Then we see respond with the status code and return with the result. Result, meaning that whole message that was a part of the exception message. So remember that when we're setting up our exceptions or throwing our exceptions from our hunters, were always sitting the message. And that message is what is getting serialized here. I'm being sent bokeh sparked off that response. Null if you want it to be a bit more explicit. Because all we're doing is sending over arrow with exception message, we could use our error details. I could have said new error details and then error message would be the message and the error type could be something else. So I would say Eric Type Tool and then probably just say failure, our error, whatever it is so you can get creative. I mean, it's up to you what, once again, this is what they would get in their response, whatever you put there. So a mode that we have finished setting up that middleware, we need to jump over to the startup file for API. And inset of the Configure sexual. We're going to tell it to use the middleware. So I'm just going to do it right above everything else. I don't want to say up DOT use middleware, exception on middleware. Go ahead and add any missing references. And there we go. So we all have our middleware trying on catching globally and then gracefully handling how it responds to any client that's his calling. I just wanted to jump back to the implementation once again and have a quick adjustment. I don't know why I had if this off initially. So with the results for the validation exception, we definitely want the exception errors to be going Buck in the body, right? So the validation messages, one thing, fine. But then we want results to be equal to the JSON serialization of the validation of the arrows, right? So once again, you have many options on how to handle the situation. So we're sitting the exception message by default, but we're overriding it in the case of the validation error. So it'll be the list of errors.
37. API Exception Handling: All right guys, welcome back. This lesson is inspired by the failure point that we saw when we were changing the approval status of the leave requests, we saw were a part of the operation was completed and the other part failed. But then we need both parts to actually work before we can update the database. So what we're doing now is setting up what we call a unit of work, which basically says that I have all of this work to do. I have all these touch points. I'm going to do all the operations and then do one final commit to the database, we will either be successful or not. So what we had was each repository saving changes on its own. So if this, if there was an operation that required two repositories to save on one side than the other fields, then we would have in consistent data in the database and we want to avoid that. So I've gone ahead and set up this unit of work contract so we know where the contracts live in your application persistence folder I unit of work. It's inheriting from I disposable. And it's basically just making reference to the three repository interfaces that we know. All right, so I leave allocation, leave requests and leaf type, and it has a method that says Save nodes implementation lives in the same folder as the implementation for the other repositories. So that is the persistence layer repositories folder unit of work. So inside of this implementation, we're inheriting from the units of work. We have our DB context being injected in. All right, and we have referenced all of the repository, so I have private fields for each repository. So misguided this file is acting like I register for the repositories that we have, want to maintain the unique repositories. Sometimes you would actually see a unit of work where it's built around the generic repositories. So, and each implementation will just be generic because there are so many unique methods in our repositories want to keep the unique repository is being referenced here. So later dome, we actually have the public property that much is each and let me just BreakLine so you can see it more clearly. So the public, I leave allocation repository, it's going to be called DV allegation repository. I'm basically we're just performing I get so we're seeing, Get me the private field and if it is null in initialize a new instance of the leaf allocation repository implementation and pass in that context that we inject it in. Because remember that each implementation has that context being injected in. All right, so that is why we have to make sure we inject in the context and then we pass it along to each one. So those three lines pretty much look the same bar in the names and the types being referenced. Then we have a dispose method which is going to be called in the background where we just dispose of the context and then suppress the final garbage collection. And then our task to save is going to have our save changes. So in other words, you'll be doing everything would be putting, we'd be adding, will be doing whatever it is. These repositories have to do individually, but then we're doing one final Save. All right, so after you've implemented that or you've replicated all of this. Let's take a look at some of the changes that are required in the other repositories. So let's start off with a generic repository. Before what we had were individuals Save Changes in the ad, the delete and the update methods. All right, so now I'm getting that Greenland because there's no longer an asynchronous call inside of these async tasks? Well, we can address that later, but my point is that I've removed all the Save Changes lines from these particular methods because we don't want them to see if we have to do multiple things. You want to add something and delete something and do an update. We want one final commit to the database. All or nothing per unit of work are in each handler. Because remember, the handlers are basically dealing with scenarios. So we want the full scenario to be completed, all or nothing. So we've removed all the sea of changes from the generic repository as well as from any other individual repository that may have had a Save Changes being used in there. So like in allocations where the average had to save changes, I've removed that. And in the leave requests we had the Save changes being done inside of that method to update, right. To change the approval status, I removed all of that. Now with those removals, we need to update our handlers to adapt appropriately. So I'm going to jump over to the Create leave requests, and I'm going to show you exactly what kind of refactoring would happen. One, we would have been interacting with two or three repositories in this handler. So would have injected I leave repository, I leave allocation repository, et cetera, et cetera. Know we only have to inject or a unit of work. And before I move any further, let me just point out that we know have to register it instead of the persistence services registration. The same place you would have registered all the repositories where we're just starting our eye unit of work. So that makes it an injectable components. All right, so now we can replace all the individual ones with the I unit of work. No, you're faced with a bunch of FRD lines. Well, know where you had the underscore leave type repository as the private field. You can now replace that with unit of work dot leaf type repository because you're giving it the same repository just through the unit of work. So this is like our register for all repositories out of me. Mention of that earlier. So I'm just highlighting all of the changes that I would have made it know that I'm using the unit of work on the square unit of work for the leave allocation repository, unit of four for the leave requests repository. And then when we have an operation that is augmenting data, we do a Save. See that? Now while I'm in this create leave request handler, I just wanted to point out one other failure point that I saw in my tests and you've probably encountered and I just want us to address it here. That is when there's no allocation because I tried to apply for maternity leave with an employee that doesn't have maternity leave. So you have two ways of dealing with that. One, you could only, you could filter that leaf types list only on leave types that are allocated to that user. So the user would never be able to apply for something they have no allocation for. And frankly, I think that that is a nice clean way to do to this creates a new repository call where you get that pointed lists and you sent, but not least, I think that's easy enough. I however, have made an adjustment in this handler in the interim to say get the allocation. So remember we've got the allocation and then we calculated the d is requested. And if the d is requested, exceeded, then we added a validation results error, right? Well, what I'm doing here is I'm seeing if the allocation is not. So with the new version of C Sharp, you don't have to see is equivalent to know the vacancy is no, right? So if this comes back as null, then we add a validation result error for the leaf type ID, saying you do not have any allocations for this leaf type. So that's a simple if else addition. You can go ahead and replicate that or you could try it on your own to get the filtered list for that particular user who is about to request leave and only display the leaf types that they're eligible for, whichever one works. Now let's jump over to the update leave requests command handler, which is where we saw the real failure points that we're trying to address NOW with the unit of work. So once again, inject only the units were forced into this handler. And you'll see that it's much more compact, right? And then each line, you just have to say units are four dots the appropriate repository and dot dot the method. Know when we're augmenting the data. Remember that we said if the request comes in with the DTO, then we go ahead and do our mapping called update. And then I'm going to call the units of work to save. All right. Later, don't if it was the change request approval and this was where we saw the fault, we'll go ahead and change their approval. And then if it was approved, then it would update the allocation. And then this one worked, but then this one field, so it was approved and the person's these did not get adopted, that caused inconsistency. So no, this person would have gone on vacation for 10 days and come back and we'll come back and see that it's still often these and broken up plie and nobody would notice. So we'll want to make sure our system is not at fault. Right. So we updated the change the approval status. Fine. Then if it's approved, we update the number of days and then we save. So if this fails, this happens in the database. Now with this change and the unit of where being introduced, it's actually a bigger factor because you'll actually have to go into every single handler that was dealing with augmenting data and one injector unit of work to probably change older references to the repositories, all of all over the place. And then three, make sure that you call the unit of work save method. Now that you've made that huge risk factor, alright? And especially when you have done that to the leaf type command handler and other points of contention that will come up is our unit tests. Because remember, at least we did the unit tests for the leaf type command handler together and know you're changing the injection, you're changing something about it. So actually if you compile at that point, you would probably get some errors in the test files because null certain things don't exist anymore. That's 12. The fact that the operation has changed in general, we need to update artists. Let's start off with the mocks because that's probably where you're getting the first error that the Mach repo no longer exists or the mockery boys no longer off the type that it needs. That's fine. So what I have done is created a new Mock file that I call mock units of work. And it's following the same principle the previous smoke, where I'm just instantiating a mock of our unit of work. It's called get units of work. That's a method. And I have mock, you will w is equal to a new instance of the mock unit of work. No, I have the mock leaf type. Let me correct that mock leaf type repo. That's what we were dealing with, right? So that's the test we wrote to test the leaf types. Know that I am using the unit of where I have to mock the repository inside of the unit of work. And I already have the code to mock the units of the elif type repository, sorry, right, So I already have that. That method doesn't have to change at all. All right. We're filling it with test data and we have done all of that. No, I need to get this Mach into the unit of work mock. So all I'm doing here is seeing mock. The type repo is equal to calling that mock class and getting the leaf type repository. And then my setup notice is that the leaf type repository will return that mock object. Then I return that mock units of work. So now in the actual test for the leaf type handler, and I've just commented out some of the lines just to show you what exactly I've changed. I've introduced a new field of type mock I units of work called mock. You will w, w unit of work. And then I have instantiated it inside of the constructor by calling my mock units of work dot a2 units or fork. Afterwards, everything else remains the same. Don't align. Well for me, 42, 43, where I'm seeing that the handler is no a new instance that takes the mock unit of work as opposed to the mock ripple has previously used and that object, then everything else will flow. So at least don't in the tests we have to say mock unit of work dot object, dot leaf type repository gets all. Similarly mock unit of work dot object dot lifo repository gets all. Otherwise, the tests will pass and then you're good to go. So once again, unit testing would show you fill up points or potential failure points in different parts of your application just in case they are, or they went through massive refactor activities. But that's really it for this activity where we're adding the units of work. I hope you see the value in it and we will continue with improving our application in subsequent lessons.
38. Handling Token Expiration: In this lesson, we are going to be setting up a global exception handling middleware for our API. So what happens is that we had custom exceptions that we created from almost the start of the project. We have been throwing exceptions at certain points in our handlers. However, we have not necessarily told the API whole it should respond when exceptions are thrown. So of course, you'll want to always gracefully fail if there is a type of exception, we want to send back a code that is indicative of the type of exception. For instance, I have updated all my update handlers to also throw a not found exception. So upon to know, let me just correct this one. So open to know. I didn't have the code. If you already had the code, then that's good. That's kudos to you, right? But we were throwing validation exceptions when the validation fails. But then what happens if the record to be found was not phone, then we want a not found exception. So I've added that on each check to see if leave allocation is found, if the leave requests, That's a boat to be updated his phone and if the leave sorry, the leaf type, their goal. If the leaf type is phone, we just throw a not found exception. Alright? So throwing the exception is easy enough. Handling it is another thing. So notice that there are no try catches on. It would be kind of overbearing forward to try and put the try catch and every single one. So what we're going to do is set up and exception handling middleware at the API level because the controller uses media to call it a handler, but we don't have any track catches here either, so we're always returning, okay? But then there are times when the exception is thrown and it Okay, can be thrown. And in the API is literally just throwing some random response buck on the client. We want to make sure that we know what is being thrown. So go ahead and create a new folder in the API project called middleware. And in that folder create a file called exception middleware. So that's our cross exception middleware. And I'm just going to walk you through what this is going to be doing on web or details. So we have a class in that file, in that other class called error details. I'm just walking you through the simpler parts first. And error details just has the error type and an error message. So at least we can always inform the client this is what went wrong based on the circumstance that we're faced with right? Now. All it said that we have inside of the class for exceptional middleware, we have private read-only field called next, and that is of type request delegate. So we instantiate that in the constructor. And then we have a method called invoke a sink. So public AsyncTask invoke async and it takes a parameter called HTTP contexts. So we've already kind of looked at what the HTTP contexts allows us to do. Basically, it allows us to see the request, the response, everything with a whole workflow between client and server is stored inside of this HTTP contexts. So this is going to be acting like an interceptor, is going to try to see. It's going to say do the next option that should be completed via the HTTP contexts. That's basically what that is doing. Her. Http contexts do your next option. If there's an exception, let's catch it and then we will handle it. Now, let's go on to how we handle it. So private task handle exception is sink where giving you the HTTP context and the exception that was caught. Know we we are just saying that we want a response punted to be application slash JSON because it's the API. So we know that everything that we respond is going to be in the form of JSON. We're also setting a default internal server error, HTTP status code status school is defaulting to that which is 500. Then we're going to say the result is equal to, I never want to serialize objects into a new instance of arrow with the exception message. All right, so we're serializing all of this into the results null when we go on to the switch. Where basically seeing what type of exception is this, because exception is the base datatype. But then as we've seen, we have our own exceptions. We have the validation exception, we have the let me go out the butter requests exception. We have the not found exception. So we can account for all of these accounting for bad request. I'm not accounting for validation. Me. Go ahead and update that. So what we're doing here now is seeing tell me what kind of exception it is. I know it's an exception, but which type was it really wasn't that bad request. An exception of validation rather owes its I'm not found. Based on the one dot to do is we're going to change the status quo. So it's defaulting to 50, 100. Meaning if it was just the unexceptional, maybe it was at database level failure, maybe it was a network failure that we can't buckle. And four, then it's definitely a 500. 500 means it's a system, the system that the API is using, it's the system's fault. However, bad requests would indicate that you are at fault as a client, but you sent me garbage data. So I'm going to tell you it's a bad request, which is a 400 year ago. If it's a validation exception, that's also kind of a bad request because you sent me but data, but you can always look through and see what other code might or better for the type of exception. All right, but then you always want to stay in the 400 range with error codes, right? So that's where we are. So I mean to you That's alone. So this one says But request and then not found exception is a 40 for meaning I couldn't find what you're looking for. So 40 for so if none of these was the case, then we just break and that's would remain as a 500. Then we see respond with the status code and return with the result. Result, meaning that whole message that was a part of the exception message. So remember that when we're setting up our exceptions or throwing our exceptions from our hunters, were always sitting the message. And that message is what is getting serialized here. I'm being sent bokeh sparked off that response. Null if you want it to be a bit more explicit. Because all we're doing is sending over arrow with exception message, we could use our error details. I could have said new error details and then error message would be the message and the error type could be something else. So I would say Eric Type Tool and then probably just say failure, our error, whatever it is so you can get creative. I mean, it's up to you what, once again, this is what they would get in their response, whatever you put there. So a mode that we have finished setting up that middleware, we need to jump over to the startup file for API. And inset of the Configure sexual. We're going to tell it to use the middleware. So I'm just going to do it right above everything else. I don't want to say up DOT use middleware, exception on middleware. Go ahead and add any missing references. And there we go. So we all have our middleware trying on catching globally and then gracefully handling how it responds to any client that's his calling. I just wanted to jump back to the implementation once again and have a quick adjustment. I don't know why I had if this off initially. So with the results for the validation exception, we definitely want the exception errors to be going Buck in the body, right? So the validation messages, one thing, fine. But then we want results to be equal to the JSON serialization of the validation of the arrows, right? So once again, you have many options on how to handle the situation. So we're sitting the exception message by default, but we're overriding it in the case of the validation error. So it'll be the list of errors.
39. Handling Token Expiration: Alright, so we're continuing to improve on our application, and this time we're shifting our focus back to our client application. So a few scenarios here that we have not accounted for, or at least we probably have encountered them, but we have not fully addressed them. Number 1, when you probably realize by now that if you logged in about an hour ago and the near Buffon your turn to use a system. They're getting exceptions from the API for 100 ones because the token that is there, even though you are assigned into the application, tolkien is expired. So the client up things, it's okay. But when you try to access the API and sending over a still token, the AICPA is rejecting it and the application does not know what to do. So you're probably getting those exception pages. And other thing is that we want to setup your own custom page for when an unauthorized browsing request is sent. Meaning you're a user, but you're trying to get to an admin page. You would have seen MIGA the error and you probably have gotten to arrow where it's trying to go to a page called Access denied, which is one of the default pages that we did not scuffled in this client application. So I'm going to show you how we can take even more advantage of the whole HTTP context, the request pipeline. We just did it with the exception handling for the API and all we're going to look at it on the client application and how we can handle these different scenarios in a global manner. So let's start off with the costal middleware that I have setup. And I'm calling his request middleware. Some calling it requests the middleware because I wanted to sit in between every single browsing requests that ever happens in our application. And then just like we saw with the API, we can always intercepted, interrogated and then make a decision as the word should go if it meets a certain criteria on. But let's start off with a folder called middleware in the MVC up and the file is called requests, the middleware. So this is going to be a public class that takes the request delegate next. And I am also injecting my local storage service goes, remember that's where we're accessing the Tolkien from at any point. So we go ahead and initialize them in the constructor, and then we have our method task, invoke a sink. So this method has one big try-catch, right? Won big try-catch. So we're going to try. And the simplest line would be just like we saw in the API, would be as simple. Try to await the next auction and then catch and handle any exceptions. All right. However, before we go ahead and allow it to try the next action, we want to make a few checks. So the check that we're doing is, well one, we're getting the endpoints. So this line EP represents the end point. So HTTP contexts dot features, get the AI endpoint feature and get the endpoints, meaning where are you going, where you Rosing tool. Then I'm going to see, does this end points have any metadata attribute? So remember when our controllers, these are basically metadata attributes, right? So does it have any metadata attribute, type authorize attribute? Well, authorized is of type authorize attribute. So I'm seeing wherever you're browsing tool, is there an authorized attributes not ever control or are ever end point has an author as attribute goes home doesn't have any users, doesn't have any. So if it does, meaning that we got something, it's not null, then we want to say, okay, since this endpoint is authorized to make sure that there's a Tolkien and even more so that this token is valid. So I'm going to say author attribute is not equal to null. Or sorry, if it is not equal to null, then get me the local storage. Tolkien, right, so go and check if it exists. And then I'm also seeing that it's valid because I'm assuming that it's valid at all times. However, if it exists, we need to validate it even more soul, right? So I'm seeing if the Tolkien exists, then get the actual Tolkien, then I'm using the security handle, the Tolkien handler. We know what we need to inject a using statement for that. And then I'm seeing Get me the Tolkien content, then get the expiry, and then check if the expiration is done. All right. So if the expiration date is less than the datetime dot 10, meaning that we have we're way past or where no past, whatever time will sit as exploration, then the token is not valid. Then I'm going to say if the token is not valid or the Tolkien doesn't exist, and notice the exclamation signs. So this could easily have been is equivalent to false, which is what I'll do to increase readability. So if either of these is false, then we want to call a method that I created down below called sinusoids and redirect passing in the HTTP context. So the HTTP contexts lives. Of this middleware, no problem, we just pass it along and then we return. So then that means that whatever option is said here dots the next option. We will never get to this one if we intercepted at this point. No another that's I'm making is if the auth attribute has the rules. So we can actually put in multiple rows, you can see administrator comma, this comma that, so as many rules as you need to check for. But in this case, I just want to make sure that if the author attributes, like we see here, is limited to only administrators and the user is not in the administrator role. Then I want to redirect to home slash not authorized. So this is when he was willing to the access denied by default. So we're just overriding it and seeing go to our custom page. Please redirect and return. So if the token is valid, but you are not an administrator trying to get to an administrator path we're going to redirect to, and that's the end of that. Now there are situations where we might catch an exception in trying to go through this operation. One of those situations would be with the API. So at times there might be some synchronization issue with the time, meaning we may deem the Tolkien to be not expired and valid. But then when he goes over to the EPA, the EPA still throws an exception seeing 40 one year not authorized. Alright, so in that situation, what we're going to do is sign out and redirect also have a similar situation to the API exception middleware, where we're seeing the exception. If it's a type of API exception, then take that action. Otherwise just redirect to the general home slash error page, redirect and break. So let us look at what happens in this Seinfeld and redirect. All we're doing, we're sending you all off the http context, just like what we would have billion to, I use a service, the time of logos. And then we're going to redirect you to the path for users slash login. So bolts of the login page and then go ahead and redirect, then we return. So that's all that happens inside of those areas. All right? And then basically that always makes these there. So if all of these gets skipped, all of that gets skipped. There's no attribute, then go ahead and circle the next request. If we catch an exception, we either go to the error page or we're assuming if it's an API exception, Senate and redirect, Of course, there might be different kinds of EPA exceptions are different scenarios with them. So you could easily have another costal method to see if the API exception is of type. This response type then go to this type of speech because that EPA exception object actually contains response code. So we can get the status code rather. There we go, we can get the status code that is being sent back. So if it's 40, 1, then sine Alton redirect. If it is maybe 500, then do this. So remember that we had setup our different types of response codes in the API. So we know we can get butter requests, we can get validation, we can get not phone, right? So based on the type of status code that we are expecting from the API, it will come back in the exception and then we can handle it. So that is a quick and dirty way to kind of handle what happens when the Tolkien's are expired or if we have different exceptions that we're not able to handle. And as you see more exceptions, of course, you can put in more handling capabilities in the middleware. No. Just quickly in the home. Some redirecting to or is it I'm redirecting to an access denied or not authorized speech. It's nothing funds it is created in the home controller and new action does is not authorized. And I just have one page that literally just says that text not authorized. That's all that's happening. There are jumbotron not authorize, so you can go crazy with your creativity. Find a nice custom PCC. You shall not pass on the net output. That's all that's really happening. So let's test this out. I'm just going to try and navigate to a page that I know I cannot get too while I'm not logged in. And then you see not authorized. Alright. At this point, we could probably see if there's a Ambrose attempt to a page where there's unauthorized tag and the user is not evil, authenticated, then redirected to the to the login page, right? That would make more sense if I'm logged in as a user. And once I get, I'm just giving ideas to hold, to control your application grows if I try to login as a regular user. And then I once again trying to get to the admin page, I'm not authorized. However, I can navigate to a page that I am authorized the Bowl Tool and I'm getting an error. So I didn't even test this one properly and it's fairly in front of you and that's good. It's good to see these failures would cause this is another exception. And it's more than likely because I have the auth rule with no The author attribute with more rules in it, right? So I'm trying to get to a place that has no rows in the auth attributes. And then because of that failure is catching the exception. So now it's going to go to the home slash error page, which is just going to say there was an error trying to complete your command. So of course that is a red herring because that's not really the issue. I'm trying to get to that page. Okay. I'm not authorized. That looks a bit better if I tried to get to one the requests create. Alright, so there it is. So whenever there is an authorization flag with more rows, this is going to throw an error. And that's what happens Because leave requests is authorized, but it has no stipulation rule. So let's just reflux to this quickly. And that was a good area to get because it inspired a far better and far more functional bits of code in this refactor. So I got rid of some of the magic strings and I think accounting far more global situation as it relates to the rules, stipulations, right? Initially, it was just if you're not administrator, then you can't get in. But then if you have multiple roles and even users with multiple roles, you want to make sure that you are accounting for all situations, right? So in this situation I said if auth dot roles or the author attributes roles is not equal to null, there are roles in the list. Then go ahead and get the user's role. All right, so we're getting the claim type role. Of course you don't need to put in a using statement for this. And then we get that value. Then I see if the list of rules does not contain that rule. All right, so multiple roles could be used to secure endpoint. A user can have multiple roles. If this user does not have any role and this is only accounting for our user with one rules. So let me, let me backtrack. This is when only one role is assigned to a user. You may need to do a bit more finished, maybe a for each loop or use some other method to find out if any of the rules of the users, in any of the rules and instead in the attributes, but right, and all we get the one rule for the user and check if the list of rules on teens that user's role. And if not, then they're unauthorized for that particular request. And that's basically it. So you can play around with this and you can see that this variable full, but if you don't write the quote carefully enough, it could throw off the application and give up a small screen of an error when there's really nothing wrong or when it's your own fault. So you want to be very careful when you're 18 quote for the middleware. But it is really very powerful.
40. Improve Data Auditing: In this lesson, we're going to be looking at improving the auditing. So, so far we have implemented some amount of auditing in our save changes where we take each entity once it's off base domain entity, we get to set the Last-Modified data and the big created automatically. We don't have to do that every time we are creating these objects to be saved, right? So what we're going to do is create a second DB context that will actually take over this auditing because what we're not doing right now is adding the names of the user or the name of the user for the created by the last modified by. So we need a way to get the user completing the action into the database automatically. So we started this activity off with a brand new file that I'm calling auditable DB context. Now it's an abstract class and it inherits from the DB context. It has a constructor that is very similar to the previous one scene in leave management DB context, except it only takes DB context, options, options, so it's not typed or it's in the other one, you would have seen it types like it actually has typing for the DB context. Well, we're not doing that this time, we're just taking options. All right, then we have our own implementation of a save changes. So this is not an override public virtual async task. Returning int save changes. It takes a parameter called string username, which I am defaulting to be system, meaning if no username is provided, then system is what will go into that username column. So you can do that if you wish. If you have a default, otherwise you can just meet that null. So once again, this is not an override. So we actually have the very same for each loop which is going through the entry in and the sensing based change trucker injuries for baseline entities. All right. And then we say go ahead and set the last modified date and the added date or the created date anytime one is being added. Now, I before I proceed, I would want to further filter this because the CI instructor is trucking everything if it's unmodified, if it's detached everything, we don't need these changes on things attire not being modified or added. So I'm going to add an additional filter to this where I'm seeing get me those entries, parse them into base domain entity. But where their states, so q-dot state is added or modified. So we're only interested in sitting these audit fields on anything being updated or created. So then I can know, say entry dot entity dot last modified by is equal to the username being passed in. All right, and then similarity, I can see the createdBy is the username being passed in. All right, so all of that just to make sure that we have the username present. But after that we're going to see var result is equal to 08 base. So BS once again is DB context. So based dot save changes async, and then we return that result. Now let's look at the changes that are required for the original leave management DVI contexts. One, I don't need to override, see if changes here again, so I can remove that completely. That's one tool I know need to inherit from the auditable DB contexts. So it's this DVI contexts is inheriting from the auditable one, which is then inheriting from DVI contexts for the auditable one is giving its own version. I'll see if changes before it will call the base Save Changes, which is courtesy of DVI contexts anyway. So with all of that done, we can now modify our units of work. Because remember, know that we've implemented that we have our C of changes happening right here. And this is called in context. With that injected in, I can go down to my save function and I can see username is equal to the HTTP contexts accessor HTTP contexts, the user find the first name for the user ID because remember that's where we are storing the user ID. So well, yes, so I'm just using the custom claim types dot UID. Another reason we want avoid much extreme CNS and convenient and clean that looks, and we are getting that value so that we can pass that username into this method, which would then jump over here, boots auditing stuff, and then save the actual changes to the DB context and return the results if we need it. So that's how you can improve the auditing. So with this auditable DB context sitting in-between the unit of work and the actual DVI contexts. There are few things you can do here. Some people actually would have like an actual auditing database or an auditing table or a set of tables. They want to log every single axon that is being carried out against these entities, not just sitting these fields, but maybe replicating them to somewhere else. All of that could potentially happen here. I'm not going to say all of it could cause there will be some limitations. But I have seen where people actually do full-fledged auditing between this, this bit of code and this bit of code, right? So unit, so for extends over everything. And then for each entity that is there waiting to be saved, we manipulate them. And then we save them.
41. Conclusion: I want to say thanks guys for sticking it out to the end of this course with me where we didn't necessarily have a fully functional EHR leave management system at the end of it. But we have looked at some advanced techniques. I know you can take away and apply it to your daily routines. For the duration of this course, we wrote code that was modulo testable, maintainable, and reusable, and we were sensitive to the solid architectural principles. We implemented the sea QRS and media to patterns. We looked at the best practices for separation of concerns. We implemented an API. We looked at how to secure it with JWT authentication and hold to consume that and use that to our advantage in a client application, we have done so much together and I am very once again thankful for your dedication and your support and you have any suggestions. Feel free to leave them in the suggestion box. And I'm always open to feedback. Thanks once again and have a great one.