 So welcome back everyone and welcome to the web app development track. I will just before we get into the we start with the topic of the of the day. I just wanted to talk about the logistics for for this week for this week. So we will meet again tomorrow at 10, and then we will take some 30 minutes to review the material go over the logistics will have a 10 minute break. And then we have two presentations and 20 minute break in between. And then after the after two presentations for the week, then we have prepared exercises for you to work on in groups in breakout room sessions and some of this exercises will use code sandbox. The test link on Slack. And yeah, and the facilitators or some of us will come and see if you if you need any help any questions on the exercises. And then we will do a recap and closing. So today is the runtime the advanced data queries and hooks tomorrow is. Yeah, we'll talk about making apps at the HSU applications generic and how to use those tools like translations and data store. And then they, they three we have performance and application security is a new topic. And yeah, the last day we have the testing and app hub, and I will also go over some of the details about the final project or project to. And by the way, I did receive I saw that. Most of you or some of you submitted your project project one, and I will review those thank you so much for for doing that I will be reviewing those during the week and give you some feedback. The project to will contain most of the things that most of the requirements for project one so if you haven't submitted project one, don't worry, you can. You can do that for this workshop as well. And it will be just expand I guess on some of the things that we will cover this week. And, but I will go over the details on on Friday. Yeah, so today again. Right now I'm just going to jump over to the just to start to get started. We'll have a break after my presentation and then my colleague Kai will will give the second part of the presentation will take a break. And then we will introduce the exercises, and then we will break into rooms and and then we'll be done for the day. So, with that, I think I will just go ahead and and get started. I saw that I have. Okay. Okay, so what is the content for today. Today we're going to talk about the upfront time. So more specifically about how to define advanced queries and how to use the upfront time to build a more robust application. If you participate in workshop one, there was an entire session dedicated to this topic and Austin gave a presentation introducing the upfront time. And so it's highly recommended that you that you watch those recordings. I showed you in the at the beginning of the introduction, I showed you that the where you can find those recordings on the developer portal. And, yeah, anyway, I'm just going to. Yeah, okay. So first, I will give a quick review about data queries and mutation. So most of the things that we already seen in workshop one, so I won't go into much detail. And the new topic or the, yeah, it's handling loading and error states and how to refresh data which we didn't cover last time. And after the break, my colleague I will go over some more advanced topics such as dynamic queries and variables and how to define define the squares. Async callbacks and how to use the data engine and use alert hook or the alert service. And yeah, part three, I mentioned that we'll have the exercises and the breakout room. So just about the exercises. Yeah, I can, sorry, I can just talk about it later. So with that, I'll get started officially. Sorry, for all the logistics. So, so first of all, what is the purpose of using the up runtime. So the up runtime allows you to access the DHS to API from a react application or DHS to platform application. And this means that when you use the up runtime, you're basically talking to the DHS to web API and interacting with the API. And remember that the up runtime is included out of the box with the application platform. So if you're using the DHS to application platform, you don't need to set this up again in your project. Anyway, the up runtime basically makes your life easier as an application developer in DHS to when you're interacting with the DHS to API. And how does it do it. So you do this through data queries and data queries are declarative data requests. And by declarative, we mean that you can make this request by just telling what we want. We want to get. Yeah, instead of saying how to get those things. So basically, through this queries, we can say exactly what data we need. And the up runtime will be responsible for combining all those needs that we are saying that we want and then requesting that from the server and getting us back a result. So in all, in other words, the, the up runtime is in charge of performing all these steps to get us what we want, which is great as it does it for you, essentially. I added a link to the documentation here but I can show you where you can find more information on the up runtime just in case. Very quickly. Yeah, sorry, you go to docs and then reference will be adding more content to this pages, but then you will see a link to the up runtime here. And this is where you can find all this. Yeah, the hooks and how to, to use this in your application. I will also talk a little bit about how to use the, I'm going to demonstrate something on the data query playground but then just a little bit. Yeah, okay. So again, this is just a review from last time. So I'll just briefly mention a few things. So as you see, the up runtime uses a react hook called use data query. This is how you can import this hook into your application. If you're not familiar with react hooks, we have shared documents with prerequisites and some resources that you can consult. If you want to. And, yeah, so but this is how you use the, the use data query hooks. So, you see that this is, yeah, use the use data query hooks are inside react functional component by by adding this line there. And what's happening here is that this is returning an object of four properties. And what this means is that basically this line will re-render your component every time that the state of that request changes. So if the network request is in progress, it will tell you that you are in a loading states. If there was an error, you will get an error. If the data was loaded, you will get data and then this is a refetch function that I'll talk a little bit more later. This is how you pass your query. And then you get some options that Kai will will also cover. Right side here we have a definition of a query. In this case, I added two resources indicators and data sets and points, just to show you that the up runtime allows you to make multiple parallel requests to the API. You can see the exact same time in the same query as you can see here, you can pass this to the, to the hook. So I'll just show you how we can check this results using the data, sorry, the data query playground, which is a DHSU application. This through the documentation, you can see this blue button here, you click there. Yeah, this, this is DHSU instance that we have provided to you, we have shared that information in some of the documents. I'm just going to go ahead and show you this. So I, yeah, okay, so if you have not seen this application before, this is how it looks like on the left, left hand side, you can define your queries. And on the right, you can see the results by choosing either query or mutation and executing it here. It's pretty easy to use. The use is chasing. So you will need to always use double quotes in strings. And so if we go ahead and execute this, you will see that we get the results of two resources. And it works perfectly fine. Yeah, and I have saved here you can just, you can edit your query and it will save up. Yeah, I just saved this before so you don't have to see me type. And I will show you, I will show you this too. So it's pretty handy and it's pretty convenient when you want to test or experiment with data queries. You go to, yeah, you can use this tool. And it's pretty cool. Yeah, okay, so then we also have use data mutation. Again, we did cover this, maybe not in too much detail, but data mutation is just a way to mutate or change information through the DHS to Web API. So basically queries are for reading information and mutations are for writing or changing the information. So if you're familiar with REST API, you may know that we have posts, put and delete. So to create you, yes, to create something that's a post request to update a foot request. You also get there's also a partial updates or some updates that spot, but it only works. It's only supported in for some endpoints so you would have to check the documentation to see to see how to use that so that basically means that if you only want to update a certain part of example, the name, then you would use this and then delete is for the delete request. So, yeah, so usually we use this request to mutate something or write something through the data mutation hook. This is a way to do it in a declarative way so similar to use data query. Yeah. Yeah, so again similar to the to the use data query hook, you also import this hook like this, you define your mutation here this is a type create example. And this is a little bit different from the use data query hook. This is actually this is returning an array and mutate is the first parameter here and this is a function that you can use or that you can call within your component to perform that mutation. You also pass the mutation definition here. And then you get the same properties like in use data query loading error data and there's cold as well. I'm not sure if Kyman covered that but yeah this is just a simple example. Sorry. Yeah, no, to some simple examples of update and delete. You call the resource, the type and for update and delete you would always need an ID and for updates you will need to specify the fields. Again, I just prepared this. This little just to show you how to create how to test again so you, you make a query. You're sorry you define a query in this case is programs we want to fetch the, the five first programs we want to get the ID and the name. So I'm going to go ahead and execute this. Now I want to create a program. And I have just saved this here. It will be. Yeah, the name will be a new program type will be creates and then I have to choose mutation. And hopefully it will work. And then we get this result you can see that is being created. To know exactly if it actually works. You would have to go back to your view program. So your data query and then you will see that it shows here. Now I want to just to quickly show you how you can delete. You can delete this resource that we just created. So the type will be delete. And the ID will be. The ID that I copied. So it works. We get 200 status code. So it worked. It deleted and we once I execute this again we should be able to not see it. So now it's gone. To update. Yeah, to, I'm just going to go ahead and create the same program again. Just to show you that we actually have to. Yeah. So I'm going to update this, this new one here. So instead of the leads will be updates. I'm just going to paste the, the ID. Oh, that's and for. For updates we need this data here. We do need to pass this data and the fields. Otherwise it won't work. So let's just make a change like a another program. And execute this and see if it shows another program here. So, yeah, so this is. Simple, simple examples of how you can, you can use. Use data. mutation. Okay, so this is a new content that we didn't see in workshop one. And it will cover some of more advanced data query features. So when you use the data query hook you get a few things back in the, in the object that's returned by by the hook. So one is loading error and data variables. So what's happening here is that react will call this function multiple times. So the data query hook can tell react to call your function again with different responses. So every time this function is called this variables loading and data are going to have a specific value. So the first time is called is going to have loading. So it's set to true. I will show you in the next slide and the second time it's called it might throw an error or return the actual data. So the first what what's, yeah, what's happening here. And the first time our component renders or on mount. Loading is set to true. So this means that the first time we make an HTTP request to the DHS to instance for, for example, and send our query. The result is that we've started a query but we haven't received a response yet so we don't know if it's an error or data or anything at this point. So yes, it is loading and that's why it's true. And loading is a Boolean variable that shows the first time it renders and the error and the data will be undefined because like I said, we didn't get a response yet. The second time it the component renders we have two options. We either get an error, or we get the data. So this is an example of what happens if we do get an error. The loading variable will be false and the error will be. We will have a string and the error. Sorry, the string of the error message and data will be undefined because of course we, there was an error there. If we didn't get an error. Then the data is actually loaded, of course, and we call this function, the second time with. So the, the variables are loading is set to false. The error is undefined and we do actually get the results from that API request here. I just wanted to actually I prepared this little application I think it's running just to show you how to show you the first render and the second and what happens. So this is my code here for this application we have an h1 tag and it's conditionally rendering this this component. So if we get an error then we will show an error in the inside the h1 or if it's loading we will see that and if not then the data. So, if I refresh the page I don't know if you can see loading first I think this is too fast. So I'm just going to show you in the first time it renders and what happens and what do you see it would say loading and then the data because we didn't get an error but there are two costs to make an error. We should now see loading and after that the error. Yeah, the message of the air. Yes. I hope this was a bit clear. This is my last slide. The advanced data query features is the possibility to refresh data. So, as I mentioned we can have a loading error and data result from the use data query hook but that only request the data once. But if you load a page with that component, as soon as that component is displayed, it will load the data from the DHS instance. And then you either get the data or an error as we saw before, but once that's done, it's done. And then it won't try to load the information again so if the data changes on the on the instance or you added a mutation type create for example, then you would need to refresh your browser in order to get that new information rendered. If you get an error, for example, it won't automatically retry to load. So here comes a refetch. And this is which, yeah, which has the behavior of updating that information after the first load has completed. And the name says it refetches the data. As you can see, that's the fourth property of the object as return. And refetch is a function that you can call anywhere in your application. So as you can see in this example, we have the button component here and we pass the refetch function to the onclick event handler. So this will trigger when we click the button, it will trigger the function and it will refetch the data that we are requesting, which is which in this case, my application application is that we're refetching. We want to refetch the resource me. And here you can see, but I have to go back to sorry about that. Okay, so when you click the button reload, we are refetching the data basically. So this is not so interesting, but we will see during the exercises when you can actually use a refetch for for more for other use cases like when you create something. When you create a new resource, then you will need to refetch the data to show to show it again or when you're deleting to, to, yeah, refetch the data from the, from the instance. This is what I have. This is what I have for today. I have a mean on my side. If you have any questions. Feel free to feel free to unmute yourselves or Okay. So I'll be talking a little bit about some other features of the apron time that make it really flexible and useful in a lot of different scenarios. So we're making the queries and mutations dynamic. So you can supply variables to the queries so that you can use them in different contexts and they can apply to different resources or different situations. Different features for when you trigger the queries and mutations using the lazy option or not. And let's see, let's see other thing. You can use callbacks so you can set up functions that trigger when a query or mutation is complete or if it runs into an error. You can also use a more advanced tool the data engine to have more control over the query mechanics if you need a particular control flow in your application. And then lastly, I'll talk about the use alert service or the hook that provides the alert service. So alerts for your application to show valuable information and is sometimes a useful thing to to use as like an error callback. Like I mentioned a moment ago. Thanks Martin. So the first thing is making the queries and mutations dynamic. So previously we've shown a few ways of using static queries where the query object is defined using literal strings and objects. But to make it more flexible and to use them in different situations we can set up the queries to receive variables when they're executed. So a few of the properties that we can use in the queries that are dynamic are the ID property. As you can see here, the params property. So you can change things like filters page size what page you're looking at what fields you're looking at go under the params category, and data property which you use for mutations. So you can shape the data that you're sending to maybe a create or update mutation using those variables. And so you can see here in this example, the ID property previously we use a string to identify a resource here, but instead of using a string we're using a function that will return a string. So the query object can take either a string or a function like this. And so you can see in this syntax here, we have an arrow function that receives an argument and it destructures the ID property from that argument and returns the ID string. So the next thing that you need to do to make a query dynamic is to supply that ID variable when it's executed. So we can see more examples here, including both the ID and the params property being dynamic. So you can see here we have just a literal ID, the 123 here. And then the dynamic one, we have this function that returns an ID. Likewise for the params property. Here we just have a literal object. So in this one we have a function that receives an argument and returns an object here. This is the arrow function syntax that doesn't do any logic but just returns an object. You can add additional logic here to process arguments in some way that you want, just as long as it returns an object with the params that you want in a similar kind of form as this one here. So we can pass variables to these queries that have been set up to be dynamic. We have two options. One is in the options argument of the use data query hook. Deborah mentioned that briefly earlier. What that looks like is when you use the use data query hook. The first argument is the query. And the second argument is this object here. And we want variables property in that object that supplies those, those values that we want those, that dynamic query to receive. So I'll just go back a slide to see the ID and the name properties are supplied in variables here. So if we go back here, we can see the ID and the name variables are received here. The other way is in the refetch function that Deborah mentioned a moment ago. You can supply those variables in here instead of instead of using an object with a variables property, you just use the variables object here. So you can supply the ID and the name. Again, those are supplied to the dynamic query. These are useful in different situations. Like, if you're receiving a value from the components properties. It's useful to add that to the variables here. But if there's something like you have some state that you're tracking in the component. You can see more often that you would add those variables in the refetch function like this. There are some other options for the timing of the queries and mutations. By default, queries will trigger immediately when the component first loads. Options will wait until the mutate function is called to execute, but you can change up that timing. And that's that's modified by the lazy property that you can set in the in the options object here. So, by default, queries are not lazy. And lazy is false. But if you set lazy to true. You're right when the component loads. And you can track whether it's been called or not with the called variable that gets returned by the use data query hook. So what that would look like is when you blow this component. Nothing would be there called is false. And so when you get to this render function. It's false. And so you would render click the load button. When you trigger the refetch, then the query is executed. And you go through the same loading error and success or data fulfilled states that you that you normally go through if it's not lazy. So that can be useful in some circumstances. Mutations are lazy by default. So I think you can set them to be not lazy, although that's a very unusual circumstance that you would want a mutation to trigger right when a component loads. And then the next feature that's really useful are some asynchronous callbacks that trigger in response to success or failure of a query or mutation. These go in the options object for the data query or mutation hook. And the on error, if you supply an on error function as a property of the options object. It will receive an error argument. And you can execute some kind of function when that query fails. Likewise, you can supply an on complete function that will receive the data from the successful query. And you can process that data in some way. So those those several features are really useful in making these queries and mutations flexible and useful in a lot of different scenarios, being able to choose when they trigger or what happens when they're done. There are a lot of variables there is supplied with. But in the circumstances that those don't completely cover all of all of your use cases or if you have some specific control flow needs in your application. There is the option to use the data engine. This is a bit more of an advanced feature that probably you won't need. In many situations it would be unusual to need it. So I'll just cover it quickly and just mention that this is an option that's available. Instead of adding these queries and mutations integrated in the react component lifecycle. You can do a more and you can use a more imperative interface for the data engine to trigger a query and mutation. You can do it comparatively and handle data that way. The interface is quite similar to the hooks in that you have this engine object that has methods query and mutate. They both receive the query or mutation object as their first argument. There are several options where you can supply variables or on complete and on error functions. But instead of integrating in the react lifecycle. They return a promise. So you can use it in a more imperative situation or outside of the context of a react component. You can use the flow of data in that way. So I'm going to take a brief pause here to go to a live demo to show some of these features in action. I have a demo setup that you'll be able to find in the repository here. When you look at the Academy workshop on day one you can find the advanced runtime demo. The final version of the code that I'll be working on will be in there for you to browse and use as inspiration. But I'll just give you a little tour of what the project looks like right now. So right now we have this little list that queries visualizations from the DHIS to server makes a list out of their names has some buttons here to glamorize the names and an option to refetch that data here. What the app looks like right now is just a simple heading and this list component here. Right now I have the prop of the list size for this component set to 10. Oh, by the way, this is a this is an app that was initialized using D2 app scripts in it. So bootstrapped using the DHS to app platform. And so here's the app structure, the visualized visualizations list. Right now, this runs a simple query for visualizations from the database. It requests a page size of five items. It requests these fields from each data. There's this query here where we'll add some other features. There is some logic here to handle the different loading states error state, not called state and data successfully loaded circumstance. And when the data is loaded, it renders a list of these visualization list item components, which will set up to perform. This includes this this name and this button. So we'll want to set up some interesting logic for the mutation that will that will add emojis to the name of this visualization and reload this list with the newly mutated name. So here we have some new some to do item set up. We can see we have this placeholder mutation object ready to be updated. We have this use mutation hooks that will add some success and failure callbacks to will also add will execute the mutation dynamically using variables related to the visualization ID and name. Here you can see that will refetch this list. So this is, this is the refetch function for this list that gets passed in as a prop of this component. And here's just a little bit of a render function for this list item and the button that will trigger this add emoji function. So we'll start with the visualizations list to make this query dynamic. I mentioned previously that there's this list size prop that that it receives from the app. And so the first thing that we want to do is make this list size here respond to that value that it gets passed to that it gets passed. So the first thing that we'll do is set up the query to be dynamic. And so instead of returning this object here, we will return, we will pass a function that returns an object. So we'll start by setting up the arguments will receive page size and set up an arrow function that returns an object. So the page size that will query will come from the variable that we have there. Next thing that we need to do is pass those variables to that query. So what we'll do down here. I just got to move that round. What we'll do down here is set up a variables property in this options object of the query to pass that page size. And that will be what we want the query to receive is page size. And that will be equal to the list size that gets passed in as a property here. So hopefully, we can see the result here. The app is passing a list size of 10. And now we are supplying 10 as the variable here to this dynamic query, and we're getting this longer list now. I'll show you what it looks like to use a lazy query. If you set the lazy option in this object to true. And we reload this page. You can see that we run into the not yet called state here. So called in this variable that's returned from the use data query book is false. And so we render this information here. What we can do is click this button that triggers refetch. And we'll get the list. So that's what a lazy query looks like. There are some situations where that's useful to use. I think we'll let this be false for now. While we're working on it. And so now our list. Pretty nice and we have the 10 list items here. So we want to set up the add emoji button so that we glamorize our visualization names. By going into the visualization list items. The first thing that we need to do is set up the mutation that will send an update request to update this visualization item. So here we have a button that will trigger the add emoji function that will trigger this mutation. So first let's set up the mutation so that we can receive the right ID for this visualization and then send the right data to it. So for the ID in this mutation, instead of returning this this literal value, we'll use a function to return an ID. And we want our data to be dynamic. And so we'll set up a function that returns data object. So we want a name and a type for this visualization type is required value when you're updating a visualization and name is is how we're going to be the name with new emojis. So now this mutation can receive variables. The next thing that we want to do is set up this mutation to use variables. What we'll actually be doing is be doing will be sending the variables in the mutate function down here. So we won't have to do anything yet with use mutation hook. We'll trigger the mutation when someone clicks on this button and triggers the add emoji. But we want to pass the variables here. So we want to send ID is equal to this visualization object that we receive as a prop dot ID. The type of the visualization will be the same type as before. The name will be this new name that we set up using the previous name with some new emojis. Just for fun, we'll log the response. So now we can try that out and see if our add emoji button works. So we can go to this one. Click on that. Nothing happened. This is always fun to debug live. This is our network here. Proper permissions. Sometimes this gets fixed just by reloading it. So we'll have proper permissions. Always funny to get a problem that you didn't have last night. Well, what we can see is that we are sending the right request to the server at the right endpoint. Maybe if I log in again using the system. This gives us gives us an opportunity to implement the success and failure callbacks to give us some notification of what exactly went wrong here. So we can go to the mutation hook here and in the options, we can add the on complete function in the options that receives the response and we can make a message write out a little bit of information from the response that we got. Right now, we'll just use an alert message or an alert function and we can make a non-error callback message will be something wrong. So now that we can see hopefully when we load this and trigger this we see that there is an error state or an error result in that mutation and so this callback gets called an error gets passed to it and we can add that error to the message. The last thing well, you can see that this would be the result if the error was not there. So let's see if I had the right permissions to do that mutation. Let me think if there's anything else that I want to try to debug that. I could look up the system credentials but I don't want to put the credentials on screen here. So for now we're just going to have to work with that error that comes from the mutation even though the mutation is well-formed. Let's try. So for these features here the success and failure callbacks are working. We have this dynamic mutation. We send the variables using this variables object in the mutation when we mutate and we refetch this list when it's successful. The last thing that I'll talk about in the slides here is using the alert service to send useful messages to the client and this is a useful thing to pair with the success and failure callbacks with those mutations. So what the syntax looks like for that is there's a use alert hook that's provided that takes a message that it will send and an options object with a number of options that you can provide to customize the alert and returns a show function that when it's called will show this alert bar on the screen to show that message. It will pop up from the bottom. It's a nice looking UI feature that's useful to use in your app and a great way to convey a message to users when something happens and so it would look like this to implement it in a simple way. In your functional component you set up the hook to receive the show function when you use alert and you provide a message here and in the options object you can provide a duration and in this case it's 3000 milliseconds so 3 seconds long that it will last on the screen and later you can show the alert by triggering the show function and what that will do is it will trigger this alert to pop up on the screen and it will last depending on this duration here and it will show this message. I can show you what an alert bar looks like so you know what we're talking about here and the documentation here you can find this in the app runtime but we'll skip ahead to this page in the UI documentation because the alert bar is a UI object it lives in the library and when it pops up it looks like this by default it has this black appearance with the info symbol on the left and it will hide after a certain duration you can scroll down on this page to see what some other alert bars and their options look like for example a success state, warning, critical and the default appearance some things will auto hide particular states change their duration by default and will not auto hide unless you tell them to and you can add actions that will trigger functions when you click on them so those are the options that you can pass to that options object here when you use your alert and they can be even more flexible by being dynamic much like the queries and mutations can be so again instead of supplying if you take a look at this user alert hook here instead of supplying a string for the message you can supply a function that returns a string likewise for the options object instead of providing the literal object you can provide a function that returns an object and to supply variables to those functions you pass them in the show when you call the show function here and so you can see this one takes a username and this one takes is current user and so when you call the show function you can supply a username and is current user as part of this variables object it will customize the alert in that way and so say Hendrick is the current user when this show is called the message will be successfully deleted Hendrick and is current user is true so the critical true object would be supplied as the options object and so you would get an alert bar with that red critical appearance that we looked at a moment ago so to show the alerts in action we'll go back to the demo and we can nicely show an alert of not having the right permissions to send a mutation so we set up the alert hook by going const show equals use alert we will pick at my cheat sheet, oh actually I don't think I need to we will make it dynamic and so we want the message to be a message that will supply to it and the we want to customize the status here and so we'll take a status object if status return true that will be the variables object if status equals error return the option that we would supply to the alert bar would be critical as the appearance otherwise we just return empty object and so we can improve our reporting down here in the on complete and on error callbacks by using this show function to show these nice alerts so instead of using the default alert API we'll do show message is this message that we've supplied that we've made just now and status would be success in the case of on complete likewise for on error we would use show message is the message that we made above and status is error so now what that looks like when we go back to the app we had success that time we can see that we get this green successful alert message with a message that we've written and it looks good let's see if it were an error it would look red with that red critical appearance that we saw and it would show the message something went wrong and maybe I can show that by messing up the resource do that load the page when we try to add emojis we get the error so that's what these alert bars look like and how you use them that's it for this demo here you can look at the finished products by going to the app dev academy and in workshop two and day one you can look at the runtime demo and you can find the finished code here in visualizations list and list item so you can take a look and browse that and see the alert the success callbacks, the mutation with variables and that might be a useful resource to look at the last thing that I'll show are just a quick tour of some of the resources that you have available to remind yourself of the syntax for these the data query hooks and the use alert so you can look at the app runtime documentation these are linked in the slides here and also in the advanced runtime I guess I'll follow the links here you can see the app runtime docs when we open that up you can look at the use data query option documentation and so you can look at the what the options are that you can supply and you can look at the different things that the query returns here so you can see called loading error data refetch and engine the data mutation is similar use data engine has a brief mention of how to use that and then you can look at use alert here to look at the options that you can supply to use alert and how you can use the show function that's all for me