image of computer screen for Brightspot back-end training webinar
Requires Login
Brightspot back-end training webinar

Looking to learn more about data modeling and back-end development using Brightspot? Start with this training, led by our world-class team of experts.

Click here to download a copy of the back-end training deck.

Transcript

Transcript

0:05

Hello and welcome to Bright Spot back in training today.

0:11

We will be going over the basics of doing back end development with the Bright Spot platform.

0:18

So our agenda today, we're going to first do a quick demo of the C MS application itself.

0:26

So you can see what that looks like and we're gonna kind of look at it through the lens of a developer.

0:31

We'll talk a bit about some terminology with Bright Spot and Dari.

0:35

Then we will review the root style guide which will also be talked a bit about tomorrow in the front end session because this is basically the glue between the back end and the front end.

0:46

And then we will do an extended exercise where we basically do all of the back and development for a new feature including the root style guide, data modeling, the view models, some more advanced data modeling with save life cycle and modifications.

1:01

And then we will do a little bit of a graph QL API example.

1:09

So we will get to the C MS demo here.

1:13

So everything in this session today is based on this training repo in github.

1:19

So this is a public repo, everyone can clone this and follow along if you want or, or take a look at the code later on.

1:27

So I've got the repo checked out here and then I'm going to start up the docker container which lives in here.

1:47

Whenever I run the time, I like to keep an eye on the logs.

1:51

So I always add that on to it so you can just see what's going on and then sort of had to wait for the different containers to start up.

2:05

Once that's done, then we can load up C MS by going to local host, just takes a few minutes or so for it to deploy the war file, which actually contains the bright spot entire bright spot build, which would also include any like custom project code that we had in this example.

2:35

And then once that loads up, we will get to the login screen here.

2:41

So for local development, including this example environment, you can just log in with any new user name and password, it will just automatically create an account for you for like a Q A box or a production environment.

2:57

So we would have to go in and create create accounts for each user and distribute the credentials for those.

3:03

So once we've logged in, we land on the dashboard, this is sort of the, the nervous center of your C MS, which consists of these different widgets here and those can be configured as far as what widgets you want to display here.

3:20

So typically, it will just be things that are useful for the editors to see.

3:28

So we've got like recent activity in here and different, you know, upcoming events and things to be keeping an eye out for.

3:36

the main way that users will use to find content is to go up here to the search and it consists of filters on the left and the results on the right.

3:46

And so we can filter down by like content types.

3:49

These each of these would be the different data models we have in this environment.

3:53

And then based on what content type you have selected, then you might get different filters here based on that content type.

4:00

So like articles have apparent section.

4:02

So we can filter by things in only this one specific section.

4:09

A useful thing for developers is is the advanced query box here.

4:13

This basically allows you to type in the same syntax that you would in your code for writing queries.

4:21

So if the filter doesn't exist here, but you know that there's an index, you can actually just go ahead and type in something here to filter against that directly.

4:29

That's useful.

4:29

The most useful thing is for finding something by ID So if I had a UU ID, I can paste that in there and make sure I find the exact piece of content I'm looking for.

4:40

And then finally, if I don't find what I'm looking for, I can go down here and actually just create a new thing.

4:44

So maybe I was looking for an image and I didn't find it so I can go ahead and create a new image here or the same drop down exists right here.

4:51

If I just know I need to create something new, I get a, a create menu right here and I can just pick something like list.

5:01

And then these actual pages that we just went to.

5:05

let's go find an article.

5:11

So this here is what we call the content, edit page.

5:16

And each content type will have its own content, edit page layout based on the fields of that data model.

5:24

You can see here, there's, I'll close this for now.

5:26

We're gonna get a bit bigger view here.

5:29

All of here are different fields.

5:31

So we've got like headlines, sub headline authors, each one has its own type.

5:35

So these are rich text fields.

5:37

This is a plain text field.

5:39

This is a reference to some other object in the database.

5:42

So I can go query for something effectively through that.

5:47

Then we've got what's called an embedded object.

5:50

So this here is an article but inside here is an image lead.

5:54

It's actually a different Java class in the back end code.

5:58

But for the editor's perspective, it's just sort of a nested set of fields so that these are useful for organizing things.

6:06

And also on your the code side kind of keeping different concerns separated.

6:11

But you can see here, I got the same kind of fields which text, plain text.

6:15

This is actually a reference field to an image.

6:19

And then there's a special like preview here.

6:21

You can actually see what that image looks like.

6:27

Not a rich text seal, but this one's got a much larger toolbar.

6:30

So there's more options up here.

6:31

You're limited to just basic formatting, but down here, you can add all kinds of stuff.

6:35

I didn't mean to go back all kinds of stuff like images, galleries and so on.

6:51

You'll see also that this is organized into tabs.

6:54

So each tab has its own set of fields.

6:58

And then within the tabs, the fields can be organized into clusters which sort of have an expand collapse.

7:04

So you can kind of keep different fields together and declutter the U I when things aren't used, like maybe these only are used rarely.

7:13

And so we can kind of just hide them out there and ignore them when they're not necessary.

7:20

Some useful things for editors to have is notes and placeholders to describe what's going on.

7:27

So for example, here's a placeholder and this is showing that there's a default value.

7:32

So if I don't type in the promo title, I'll get this default value as this field.

7:38

So I can save time by not having to type in every single field.

7:44

And here is actually a note.

7:47

It's a note which actually has the preview image itself shown here.

7:51

So if I don't select an image here, then I get this image as to fall back, you can also use notes to explain the purpose of the field like this one, what's going on or just describing general things to the editors just to guide them.

8:05

So as you're doing your data modeling, keep in mind, you know, would it be useful to have a note here explaining in more detail how this works or things like that?

8:20

We also have what's called, what are called widgets.

8:23

So these, all of these here on the right hand side are widgets.

8:26

We also have widgets down here at the bottom and then you can have widgets up here at the top.

8:31

Those are pretty relatively rare.

8:34

The typical widgets will be on the side here.

8:37

So we can widgets to control the, the URL S for this piece of content, which site will own the content.

8:43

We'll talk about sites a little later, I think.

8:48

And then you sort of the edit history of this piece of content is as people have changed it over time, we can see what the status of it was at different times.

8:58

And then finally here we've got the preview menu or preview area.

9:01

So I can, this is sort of a live view of the content as I'm editing this, let me make this a little bigger actually.

9:09

So you can see it as I added it here.

9:16

I'll see on the right hand side that the content will sort of live update as I'm typing.

9:21

So the editors can see, oh, if I make a change here, what's it gonna look like?

9:25

And this is an important thing to keep in mind because for this to work, it's basically using the exact same code that you would use to render your content normally.

9:35

So for example, we have a required field here.

9:40

So you might think, OK, well, the field is required, then I can be guaranteed that it's, it's not null except for preview to work, you know, when you create a new article, you haven't had a chance to fill anything in yet, but you don't want preview to just be totally broken.

9:53

So in your code, you're going to have to keep in mind that even though a field is required, I still need to handle the case where it's not, there's no value and, and feel gracefully and not just have a big error show up or something or blank page or whatever would happen.

10:09

It's also useful to note that for required fields.

10:12

you know, it's possible to change that over time.

10:15

So maybe we decide a field needs to be required.

10:17

But then all the existing data was made before that was a requirement, you know, it was, it was required.

10:23

And so you would, you would have basically a sort of data problem.

10:25

You have to make sure you work around.

10:27

So required is as you see, it is more of a guideline to the editors rather than a like a really firm database constraint.

10:39

One other thing I wanted to show is sort of we have a couple of different fields that have.

10:44

Oh yeah.

10:45

So this is as I work, it'll save my progress.

10:49

So I don't lose it if the internet connection goes down or something.

10:52

And so it'll tell me that, oh I, I made some edits here.

10:55

If I don't want those, I can clear them out.

11:00

some fields have special handling.

11:02

So if we go to this image, you can see here, we've got a this is actually a reference to this image in our storage area which could be some kind of S3 or some other kind of object storage.

11:17

But there's a special view here where I can actually do some edits to the image and control like the crops, how it will be cropped at different a different crop sizes.

11:28

So this is sort of a special, a special handling of the image field.

11:32

Specifically.

11:34

dates also have a little bit of a special U I.

11:37

I don't think I have a date in the actual content, but if I go up here, I can change the published date.

11:41

And so here I actually got a date picker and pick they can pick the date and the time.

11:46

So if you have a date field just in your day in your regular data, you'll have the same kind of experience for it.

11:54

Another useful thing for developers is if you go in here to the developers tab, you can click view raw data and this will actually show you the raw JSON value of the data in the database.

12:08

And then on the advanced tab, you actually get some extra, more advanced information about the status of it.

12:13

So those are useful for debugging.

12:16

Another useful thing here is these little question marks that will show you some advanced stuff about like what, where what class this field came from and the actual name of the field so that you can kind of figure out where something came from.

12:40

So I didn't talk about So some fields that are references can be like a single select and then some can be a multi select So this, the difference here is, this is just a reference to a section.

12:52

This would be a reference to a list or a set of sections.

12:56

So that allows you to have multiple T is the same way.

13:02

So when we get to the data modeling, you'll see a bit more about that on the, on the Java side.

13:07

The next thing we're gonna look at is the sites and settings area.

13:10

So if I go up here to the hamburger menu and then in the admin cluster here, I can go to sites and settings.

13:19

So one thing to understand about how Bright Spot organizes your content is, it's grouped into sites and the site is basically a bunch of web pages that all share the same basic URL.

13:29

So in this case, we have the Inspire Confidence site and the URL is just everything under local host.

13:34

So this might be something like Bright spot.com or something like that.

13:37

And then you'd have all your pages underneath that.

13:42

And so on a site, you can have other fields.

13:46

So a lot of them come out by default.

13:48

And then there's a way that you can add additional fields here and then you get the same kind of experience where they can be grouped into tabs and then within the tabs, they can be grouped into clusters.

13:59

And then finally, we have global settings which are again, just settings that apply to the entire instance of bright spot as opposed to only a single site within it because you can have any number of sites.

14:13

And then for some of these fields, there's a fallback behavior where you can set the value on the site and then it will fall back to the global value if there is no value set.

14:23

So that way you can kind of simplify configuration of your, of your environment.

14:34

See, I think that's everything we're gonna cover here.

14:38

And obviously, we'll see a lot more of the C MS as we go into the the relevant exercises.

14:49

OK.

14:52

So Bright spot and D you might hear these terms mentioned in various areas.

14:57

So bright spot is many things, but in our case, we're going to be talking specifically about it's the C MS.

15:04

And so that includes things like the editorial user interface, the request handling and view system, things like permissions and work flows through a managing editorial, you know, work within the C MS.

15:19

And then in contrast, Dari is a framework that powers Bright Spot and provides lower level things like the database, the data modeling techniques that we use and then lots of API S for creating the database for storing and retrieving files from object storage, managing tasks and so on.

15:39

So usually if we don't, it doesn't really make too much of a distinction whether it's bright but, or Dari because they all kind of get bundled together.

15:45

But sometimes you might notice the word Dari and like a package name or something.

15:49

And that's what's that?

15:49

What's that is what that refers to?

15:57

ok, so let's do a quick tour ba code based tour before we get to the fruit style guy here.

16:10

So a typical layout of a bright spot project We use Java is the language that it would be written in.

16:20

We use Gral to build the project.

16:22

So it's organized into what great L calls projects, unfortunately.

16:27

So overloaded term there.

16:28

So each of this, this core and web in front and they all projects within Graal projects within this Bright spot project.

16:40

So the main, the main project for AAA backend developer is gonna be the core area where I'm sure this in intelligence it might be easier to see.

16:50

So this core project here is where all of our Java code is gonna live.

16:54

So you can see here, we've got a ton of packages.

16:56

This is where all of our data models are, where all of our code for rendering the front end is lives.

17:03

Then there's this front end project which is where the root style guide lives.

17:06

So this style guide directory here is what we call the root style guide and we'll talk about that in a minute.

17:11

And then for traditional traditional setups where you are going to be rendering out html, you would have a bundle or one or more bundles where you all of your front end code lives.

17:24

And this is where your templates and javascript and, and styles will all be underneath one of these bundles.

17:31

In our case, we only have the one bundle.

17:34

And then finally, we have the web project and this is what bundles everything together, Your java code, your front end code into a war file.

17:45

And so typically, you wouldn't have any java code underneath this, that would all be under the core directory and web is just assembling everything together.

17:59

So then let's talk about the Roots style guide.

18:02

So this is sort of our api contract between the back end and the front end specifically for the traditional rendering approach where someone's gonna request bright spot to render a page and we'll render back out html.

18:18

So originally this lived in the project root directory, which is where the name comes from, but it's been reorganized now into the front end directory.

18:27

Although if you're having an older project, you will still see it in the route.

18:33

And then style guide is again, is sort of an overloaded term where you'll note back here in the bundle, for example, there's actually a directory called Star Guide as well.

18:43

So this is not to be confused with the root style guide.

18:46

And there's also a program that we call, refer to as a style guide, which is used by front and developers to actually do their development.

18:55

So kind of overloaded terms.

18:57

So here we're specifically talking about the roots dog eye, which means the where we're gonna specify the views for the back end to generate and the front end to consume.

19:08

And the view is a collection of fields and each field has a specific type.

19:13

So it's strongly typed.

19:15

So it's clear, you know exactly what we're expecting in each field.

19:19

In each file.

19:21

The view consists of a JSON file and a Haars file.

19:26

The handlebars file should be empty though, it's just a legacy why it has to exist at all.

19:31

The main purpose of it is to the name of the handlebars file will be the name of the view.

19:39

And then in that Jason, all you would then have all your fields specified.

19:44

It also supports what we call groups which allow a field to support multiple types.

19:48

So maybe we have a media field, we want to be able to put an image or a video there.

19:52

And so it allows you to kind of do that joining where you can have either type fit into that field.

20:00

And then the end result of all this is we get java interface for each view we generated automatically and then we can implement that view interface on the back end by writing a view model.

20:13

And that will then allow us to supply the front end with whatever they need to render out a web page.

20:22

Something to keep in mind the roots dog I is meant for use with handlebars bundles.

20:28

So if you're doing a headless project with graph QL, you probably should not use the Roots Dog Guide because it wasn't designed for that purpose.

20:35

And we talk about graphical later, we can get into some examples of why.

20:39

But depending on your circumstances may make sense to use the same views for both handlebars and graphical.

20:45

If you're doing maybe like a hybrid approach or something.

20:49

So it's gonna be context dependent but definitely should not expect to be using the Roots Dog Guide if you're doing headless.

21:00

So I don't see any questions so we will move on to our exercise.

21:08

So we have some requirements, we're gonna be building out a recipe feature.

21:14

So the editors need to be able to create recipes, search for recipes.

21:18

They want to feature recipes and in an article.

21:27

And then they also want to be able to put a tag recipes so they can organize them.

21:37

Oh, someone says they have a question.

21:39

If you have a question, just go ahead and type it in the Q and A and then I can answer it Definitely we can translate those requirements into sort of bright spot speak.

21:48

So the editors are gonna be able to create the recipes, which means we do have a stand alone recipe content type, so they can actually something to work with.

21:55

And then we're gonna kind of interpret a bit into this.

21:59

We're gonna actually have a separate content type called recipe article, which will reference the existing recipe.

22:05

This is gonna be analogous like a recipe blog where you might have like at the top, there's a long story about how they developed the recipe and then at the bottom is where the actual recipe is.

22:14

So like our recipe article would be all that content above the recipe.

22:21

Then we're gonna build out a recipe module mainly just so you can see the module system in bright spot and can explain how that works.

22:28

And then we're gonna make this new content a new tag type for recipe rather than using the rather than using the existing like default tag.

22:42

So the question is, which dog I'd be considered a front end bundle?

22:47

So this is where I meant by, it's, it's sort of an overloaded term.

22:52

So the, the root style guide is not a bundle, it is just an API contract within a B within a theme, which is where you have all of your front end code, javascript and styles and templates and whatnot that would also have something called a directory that is called style guide.

23:14

And that would be probably what you're considering of as a theme in Drupal.

23:21

But, you know, the roots do at itself is just an api contract.

23:23

It doesn't have any any way of rendering out actual template, html or anything like that.

23:29

It just describes the data that we want to be sending to the front end.

23:36

OK.

23:37

So then just a reminder, all the code that we're gonna be looking at is in this training repo so you can look at it later if you want.

23:46

So part one, we're gonna be doing the Roots Dog guide work.

23:52

And then sort of the beauty of this approach is once we've written the root style guide, then we've kind of decided, you know what the contract is and then front of them back and more comparable after that point.

24:02

And they, they're basically independent from once we've decided the roots do guide.

24:08

So we're gonna make two new views.

24:09

We're gonna call recipe module and recipe article page.

24:13

The recipe module is what you would actually think of as recipe.

24:15

It'll have you the ingredients and the directions.

24:19

We're gonna build it out as a module so that it can work with both the module ecosystem that bright spot has out of the box as well as we can, then reuse that same code specifically on a recipe article page.

24:30

And so since we wanted to use, be used as a generic module, we need to add it to the modules group.

24:35

So that it'll just work anywhere else that supports modules and then we're going to sort of copy the existing article page and just add a recipe to it.

24:45

So let's go take a look at some actual code and then I'm going to build that.

25:01

So we can actually see the changes came out of that.

25:06

Actually, so what we just added or a couple of new files.

25:12

So we'll take a look at them over here.

25:14

So we have the front end directory.

25:16

We have this roots style guy here and then we have we added a new directory called recipe.

25:24

We have a couple of new files here.

25:26

So here's the recipe module and you see we have the handle bars, file and the JSON file standard convention is to name them the same, the recipe module for both.

25:36

But this name here is what will actually show up in the java interface that is generated from this.

25:41

So we could actually name this file whatever we wanted to if the name doesn't matter.

25:45

But for consistency and simplicity, it's good to keep the name the same.

25:50

So the json file consists of a javascript json object.

25:57

And each key here is describing a field in the in the interview that we're gonna have and keys that begin with underscore have a special, you know, sort of like meta purpose, they're not data fields, they're describing the view itself.

26:11

So in this case, we're saying we're going a template that's called recipe module, hps that's pointing to this file.

26:17

And that will then result in a recipe module view as the, the javascript, I'm sorry, the java, the java View interface that gets generated.

26:28

And then here we're gonna have a field called title and then the value here describes the type of the field.

26:34

So in our case, it's gonna be group.

26:38

So it's gonna actually be multiple things.

26:42

Whereas like down here, difficulty prep time, interactive prep time, these are all gonna be strings.

26:47

And I can actually put an example here like what the value is gonna be, but the value doesn't matter all that matters is that it's a string.

26:53

So this is just for documentation purposes.

26:56

If I put an actual value here, it won't show up anywhere except I think it shows up in the in like the Java Java doc comments on the view interface.

27:10

So basically, we got a bunch of string fields and we've got these group fields.

27:13

And so we talked a bit about groups earlier.

27:15

So the groups are traditionally live in this group directory here.

27:19

So we've got like rich text elements small.

27:21

For example, let's take a look at that one.

27:24

So these are also JSON files.

27:26

But in this case, you can see that it's a array rather than an object as the top level thing.

27:31

And so basically, each value within this array is one supported type.

27:35

So we support text, we support the link enhancement and we support the text substitution.

27:41

And those are the three things that can go into rich text elements small.

27:46

And you'll see we've got a couple of different ones.

27:48

We've got medium for more things.

27:50

We got large and then we have the tiny.

27:57

So you can see here, we've got some different fields.

27:59

We've got tiny, we've got small, we've got large and these just say we're going to support different enhancements within each of these fields.

28:08

The image here is a bit different.

28:10

This is actually a template.

28:12

So rather than including a group and we can support any number of types here.

28:15

Here, we're only supporting one type in this case, it's the image template.

28:18

So this just points to some other template within the root style guide in this case of the image dot HPS.

28:24

And so that will be just supported there.

28:30

So then for recipe article page is a bit more complicated.

28:37

there's include helper here.

28:39

This is actually going to sort of like an inheritance pattern where we have another view called creative work page and we're gonna then basically include all of the fields from that and then add some additional fields.

28:49

So recipe article page is actually gonna extend the creative work page in the view on the view interface side.

28:55

When job actually generates these, we're gonna have recipe article page, extend this creative work page effectively.

29:04

So we got a couple of fields here.

29:05

We got the lead which again, we're going to be using the group, we have different number of leads that we support.

29:11

The article body is going to be the rich text elements large.

29:14

And then here finally, we're going to point to our new recipe module, template or recipe module view here so that we can have a recipe module within our article and the filing, you know, here is this underscore or false.

29:29

So this is a little complicated.

29:31

This plays into the bright spot preview.

29:35

So by default, bright spot assumes that these, each of your views is like a module.

29:42

And so it's if you wanted to preview, you would need to then place it into some page that provided the actual markup.

29:48

So you know, the header and the footer and all that other stuff, whatever you need to actually make it like a valid html page and be able to see it in, in your preview.

29:58

But for things actually our pages themselves like recipe article page, we don't need to be wrapped up in that extra market because we're, we already provide the html tag, the head tag, the body tag and all that within our own template.

30:12

So when we put underscore W or false, we're telling the system just to not wrap this up into anything, whereas here we're using the default value.

30:22

So it will actually get wrapped up into something and we'll talk a bit later about a preview as well on the back side and kind of see angle of that same thing.

30:31

But that's just sort of notes anything.

30:33

It basically if you're doing a page which has its own fully valid html markup in its template, then you would have wrapper IW Rapp or false.

30:43

Otherwise you would just not have this at all and just leave that out like that and then it would be wrapped up as necessary for previews.

30:55

So that anytime you make any changes to the root style guide, you need to build the front end project, which is where that style guide lives.

31:08

Actually, so I can tell great will just build that one project and then that way, then it will have populated those.

31:20

Basically, it processes all of these json files and haars files into the java objects.

31:28

So if we do a search now, I should be able to find to be module view and then this sort of has a 1 to 1 correspondence with that, that JSON call we just looked at.

31:41

So for instance, we had like the cook time and the difficulty, those are all strings.

31:47

So here they are returning to sequence.

31:51

We had directions which was the large rich text field.

31:56

So you can see here it's iterable of this, which is what we call a field interface.

32:02

So this is how this actually ends up working is each group basically gets each field that supports some other view, whether it's a group or whether it's just a single view gets a field interface associated to it.

32:15

And then anything that then fits into that field will then implement that interface.

32:19

So in this case, we've got a bunch of different templates views that all fit into this one slot, that's how it stays strongly tight.

32:34

So then for image, for example, we said they're only, we only support image view.

32:37

So here it is only image view supported here.

32:44

So see anything else we should look at in here.

32:51

Then we also got a recipe article and I remember this one, we said it was gonna extend creative work page.

32:59

So here we are extending that.

33:00

So that has a bunch of its own fields.

33:02

So we just get all of those and then we add on our article body, the lead and then the recipe and then five is going to tie it all back together So recipe, remember we go into the only recipe can fit there.

33:15

So then here we go, we've got recipe module view and that's where this stuff comes in here.

33:20

So recipe module view extends all the field interfaces that it will fit into.

33:26

So in this case, it's only recipe article page for now.

33:30

And then it also extends this preview page, main view, main field.

33:33

And this is what that underscore wrapper stuff ties into.

33:36

So anything that doesn't have underscore wrapper false will extend this value here.

33:42

And that way, then it will actually work into this preview page view which gets generated automatically for us.

33:48

And that way we can then use that to preview this module within bright spot C MS.

34:02

A little thing to note here is these annotations that got added here.

34:05

So this is what tells Bright Spot how to actually line this up on the front side.

34:09

So we're telling it that it's gonna use the recipe module in the bars template and these are kind of duplicative, they both sort of mean the same thing.

34:16

And then the interface here tells bright spot that all of these public methods on this class.

34:22

So the interface, every method is public.

34:24

All the public methods on this class can then be sent to the front end.

34:27

We'll actually use that later when we get to the graph QL example.

34:34

So our next step is we talked about, we want to do the, the recipe module to work with just the general modules ecosystem.

34:48

So what we did is we're gonna add it to the modules group.

34:55

So here is a list of all the different modules that we have in the system.

34:59

It's like fa Q figures like an image forms and so on.

35:05

But down here toward the bottom, we've got recipe module, we just added this here.

35:12

So anywhere we can put any module, we can now put a recipe module as well.

35:15

And since I just built that for the recipe module to use, so now we can see here's all the spots, you know, before we just had a preview page main field and we had recipe article, recipe field.

35:29

So now we got all these other fields.

35:31

These are all the different places that modules are supported.

35:32

So container modules basically have more modules grouped into columns.

35:40

There's different slots on the page template to put modules above the side and below the main content area.

35:47

You can put modules in the footer, you can put modules into tabs.

35:52

So this is all the spots that modules will fit.

35:54

So basically by just adding that one little snippet to the modules group, now we can just be able to put this anywhere we want to with modules.

36:10

So our next step is to do the front end bundle in this case, we're not going to cover the actual code for this.

36:18

tomorrow, we'll have a front end session where we discuss how to do front end development.

36:22

So right now we're just gonna, I'm just gonna apply a commit that has all the changes necessary.

36:37

I'm gonna build that in the background so that we don't have to wait for it later.

36:42

But basically, we're gonna add the bundle code now just so we can have a nice preview and in the C MS as we're doing our development.

36:49

But depending on how your team is gonna be set up, you might have to do the back end development without the front end being done.

36:56

In which case, you wouldn't be able to do see the preview and all that.

37:00

So we'll talk a little bit about how you can still do some testing and whatnot even if the front end isn't actually in place yet.

37:06

But for now, we're just gonna have it in place just to simplify things.

37:13

So then we're gonna move on to data modeling.

37:19

So any content type that you want to be able to save to the database would need to extend the record class which comes out of the DARI system.

37:28

And that enables DARI to serialize the data, save it to the database.

37:32

And then when you want to get it back out of the database to turn it back into a java object.

37:40

And then there's this class called content which extends record and adds some additional capabilities for full tech search.

37:47

Some additional widgets will display in the C MS and then it will change the save button to publish.

37:57

So kind of a general rule is anything which an editor should be able to find in the C MS search should extend content.

38:03

And anything which should be able to independently create, which kind of means the same thing as what I just said.

38:07

But if you want to be editors, we want to be the editors just go create a recipe, then it needs to extend content as well.

38:14

If you need full text search, but you don't want the editors to be able to just see it in this regular search.

38:19

Maybe you just need full tech search for some requirement, but you don't want the editors to create them just really knowing.

38:24

you can add the searchable interface directly onto your class and possibly the exclude from global search, which will then hide it from the global search.

38:36

So let's actually get jump into some code here now and I get that building so we can actually take a look at it once I finish talking about everything.

39:01

So we're moving now into the core project.

39:07

I created a new package called recipe and here is our recipe data model.

39:16

So it's a Java class extends content because you want the editors to be able to create them.

39:23

And then we have a bunch of fields here.

39:25

And each field here will correspond to a field that will show up in the C MS for the editors to, to interact with.

39:34

So the, the type here of the field here will then correspond to the type that shows up in bright spot.

39:42

And then we can use annotations to control how the field should behave or what kind of restrictions and whatnot it has.

39:49

So in this case, we have the title field, it's gonna be index which means we can then query recipes by title.

39:55

It's required, which again, remember is a more of a guideline to the editors that they should provide a title rather than it's actually a database requirement, although it will check.

40:04

So when they try to save without a title, it will throw an error, display an error to them.

40:09

And then we're gonna say this, this field is gonna be rich text.

40:13

And then we can control the toolbar that we'll display.

40:16

So this case is gonna be the tiny rich text toolbar.

40:19

And then this field here is also strain but doesn't have rich text, which is gonna be this, this will this will be a plain text field.

40:27

Here difficulty is an enum.

40:29

So we've got a fixed set of values and so this will actually display as a drop down and the editors can pick one of the three values.

40:36

Here's some more rich text fields with different toolbars and they have this in line false.

40:41

So up here, the default value is in line true and this controls block level elements, whether they're allowed or not.

40:49

So in this case, ingredients and directions can support things like images which are block level.

40:55

Whereas the title only will display in line markup like bold metallic and so on.

41:03

When we get to the rendering side and view models, this also comes into play a little bit, we'll talk about that.

41:11

This is an image field.

41:12

So this is actually gonna reference another object in the database.

41:17

So this will be like an object picture of the editors can go search for an image and, and, and reference it.

41:24

Here's a list of recipe tags, recipe tag is a new thing.

41:26

We'll talk about that in a minute.

41:29

So this case, this will be zero or more references to other objects in the database.

41:34

So the list allows us to have not just be restricted to one value.

41:42

And then we've got some numeric fields for the different cook times.

41:47

So these will actually look like text fields in the C MS, but they'll be restricted to only integer values.

41:53

And then we've got our override so that we're gonna have a fall back here where the total time will either be the sum of these three or if they want to type in a specific value they can do.

42:02

So.

42:04

so then we've got all our getters and setters, these are just standard.

42:07

you know, you can use your ID E just have them generate them automatically.

42:13

The one thing to note is as a best practice, any kind of collection field or map that list, all those kinds of things, the best practice to never return.

42:22

No.

42:23

And so we just just check to see if it's empty or not rather than having to know or empty or no, you know, something else.

42:31

So the best practice here is to just always check if it's no, it's not if it is all just to find a value and then return the value so that we don't have to do an old check on these.

42:42

And it's best to assign the field.

42:44

We could, we could have just done, you know, return to array list.

42:49

But now this array list that we've returned is not linked.

42:51

And so if someone was to add test tags to it, they would just be lost, they wouldn't get saved to the database.

42:56

So it's best to assign a new specific value to the field and then return the field so that everything stays in sync.

43:04

And so I recommend if you don't have it already that you configure your ID E to just generate these automatically, probably find templates online to set that up.

43:13

Just, you don't have to think about it.

43:14

You just like credit the credits and setters, you don't think about it.

43:16

They're just always going to be right.

43:20

So we've got some additional business logic here.

43:24

So we talked about the total time gonna be the override or the fallback.

43:31

And here's our logic for the fall back.

43:33

It's just it's more complex because each of these values could be null.

43:37

And so we'd have to make sure that they're not null.

43:39

We always sum up the non null values.

43:46

I guess recipe tag pretty basic, just have the name and the internal name.

43:53

Let's take a look at the actual C MS now.

43:58

So since I built that in the background, the the docking container will basically watch as you.

44:07

if you build the war file again and they'll redeploy it.

44:15

So now if I go here, I can actually create a new recipe.

44:20

And you can see here we have a direct correspondence between our Java class and our continental form.

44:27

So we had a title.

44:29

It was Rich Text with the tiny toolbar.

44:30

And so here we have a title, the rich text.

44:33

The internal name is plain text.

44:35

The difficulty has enum drop down, got some more rich text fields.

44:39

We have our image to go find an image somewhere.

44:43

We can have references to recipe tags because I have some from the last time I ran this training.

44:48

So I've already got some data populated here.

44:51

And then we have our times.

44:54

So some things to note here.

44:55

So like if I don't, if I type in like invalid text here, it's gonna say, you know, it's not an integer.

45:06

We also have our error here because I didn't type in the required title.

45:15

And then if I actually go and fill some of this stuff in here, we have was able to save it.

45:25

So there's some things to note here.

45:30

The internal name, it's best practice is for this to show up as the label up here.

45:37

This is basically for the editors that they have some naming scheme they want to use to organize recipes.

45:41

This will show up.

45:43

We can have this show up as a label here.

45:44

It also shows up here as the label in the search.

45:49

So we should move, we should configure this to display this value here.

45:52

And then if there is no internal name, we should have it fall back to the title just so they don't have to type it in if they don't need to.

45:59

And then it would be nice here if we showed that, you know, there's a default value here, which in this case would be 45.

46:05

So they don't type in a value look at the default.

46:10

And then here we've got a URL, but we're actually going to use the RSP article page as the URL that's gonna have the URL for the recipe.

46:17

So we actually don't need this widget, we should hide this.

46:23

So our next commit, We actually gonna do some clean up.

46:35

I, I build that background.

46:40

One thing I forgot to note is here, Bright Spot supports both capital I integer as well as the lower case I integer.

46:55

The distinction in the Java ecosystem is that capital I integer can be null, whereas the lower case integer will default to zero and cannot, cannot be NLL So you, you can do either one you want.

47:08

Typically I will do the ones that can be null.

47:11

So we can signify that whether the editor is actually provided a value or not.

47:16

Basically, we can distinct between null and zero effectively is what that means.

47:22

The same applies for bullion fields.

47:24

So you can do capital B bullion and lower case B bullion.

47:26

In that case, I recommend that you use lowercase B bullying.

47:29

So you don't have to run into issues with like three way logic.

47:31

But the, the bright spot U I doesn't really let you pick the null value once you've selected true and then unselected, that becomes false, there's no way to get back to normal.

47:42

So typically I think it's just easier to use lowercase B bullying and avoid any any weirdness with that.

47:51

So, so we've done some clean up here now and improve the UI a little bit.

47:58

So first thing here is to note is we added this no URL S widget and this is sort of a hack.

48:03

So bright spots provides this content edit widget display interface.

48:07

So this allows you to control which widgets will show up on your content type you get passed in the class name of the widget and you can return true or false to have it display or not.

48:17

But we found that like 95% of all the implementations of that were just hiding the Euro's widgets.

48:24

So we went ahead and just made a I know your widget that just does that work for you.

48:29

So you don't have to copy this around to every single class.

48:32

But if you need more than this, you can just extend Contin widget display directly and, and control what you want to display from that.

48:42

So that's gonna hide the Euro's widget for us, which you don't need.

48:45

Then we've added a placeholder on the internal name.

48:49

So we've got, we've got some methods here with the logic for generating the placeholder which is going to be the title.

48:57

but remember title is Rich Text and the internal name is plain text.

49:00

So we have to convert one to the other so that we don't end up with markup in our field from the rich text that we don't want to see.

49:10

So we use the, this method to do the generate that placeholder value.

49:16

And then we also overrode this get label method which allows us to control the label.

49:20

And so in our case, it's gonna be either the internal name or the internal name fallback.

49:27

And this is utility method just, just does that logic for you if the internal name is null or empty or blank, typically, we conflate all those into being a non value because it's, you know, in the C MS, it's hard to tell the difference between this and this, right?

49:47

And you can't really see it unless you're really looking for it.

49:49

So typically, if it's just, it only has white space, then we consider it to be no.

49:59

And then we've also added a placeholder to the title time, total time override.

50:04

As well as the explanatory notes on all these deals to say that there in minutes because it wasn't obvious before.

50:11

So here's our placeholder.

50:12

And again, we have that Fallback logic already encapsulated in our total time fallback method.

50:19

So we can just use that directly here.

50:22

I've also organized these times into a cluster so they can be hidden away and then they took up a lot of room here.

50:30

You can see, like we got a lot of real real estate attached to these fields that are just, you know, never gonna hold more than two digits, probably, maybe three digits in a very strange recipe.

50:41

And so we can kind of compact that a bit to save some space and we can do that by adding this is third class onto the fields which basically lets them stack.

50:53

So if we have different fields that all is third, then we can fit three on a row.

50:58

There's also is half, which means we can fit two fields on a row.

51:04

And then as long as the fields are next to each other, then they can stack like that.

51:08

Otherwise they'll just still be full width.

51:17

I think that was all the changes, make sure I didn't miss anything.

51:25

Oh We did this thing for recipe tag as we tag on the same issue where we didn't, we don't need the widget.

51:33

They, they're not gonna have pages and then we added the placeholder and the label so that we can use the internal name as well.

51:40

So now if I go here, so it's already built, it should reload.

51:48

And now you can see that I've got my internal name.

51:49

It's now showing up here.

51:51

And if I delete that, now I get the fall back and the placeholder, we've got our timing compacted into a cluster.

52:00

We've got three things on the same row, we've got our notes displaying it's some minutes so the editors don't get confused, try to type in fractional hours or something.

52:12

So, yeah, just sort of keep in mind, you know, as you're working, the editorial experience is paramount, you want to make sure that it's easy to do things.

52:23

Common things are on the main tab.

52:25

Commonly access fields are on the main tab.

52:28

Rarely access things are on different tabs, group things in a cluster so they can be seen together and, and hidden when they're not necessary.

52:38

He added placeholders, notes, hiding things, making a useful label.

52:46

OK.

52:47

So let's talk about the recipe article now.

52:59

So we've added the recipe article class here.

53:03

And what I did for this just to be simple is I just copied the existing article class.

53:08

It comes with a bright spot.

53:11

So it's just that same class.

53:13

But then I've added this recipe field here.

53:19

Typically, if you're going to have multiple article types, you would make like an abstract article and kind of move things into that that are common among all your articles and then have a recipe article, regular article, special article, whatever kinds that you would need, I'll extend that one.

53:34

But just to keep things simple, I just copied the class directly.

53:39

because the real focus here is the recipe, not the article and then we added her, just get her inside her for that.

53:46

, reload the C MS here.

54:00

One thing you can watch for here in your logs is this line here, deployment has finished of your war file.

54:08

Once you see that line, then you can go and reload the C MS, it will actually trigger the rest of the C MS to start up.

54:19

So now I've got my recipe article and then I can find my recipe, put that in a section.

54:38

Two.

54:45

So not much to talk about here.

54:47

Just basically we have a recipe field.

54:49

It's a single, it's a reference to some other thing in the database, we can go find it.

54:56

So let's now move on to the modules.

55:06

So I'll explain a bit about the the module ecosystem and bright spot.

55:13

So here in our module recipe, we've got our recipe module.

55:19

So typical approach here is we talked a little bit about embedded objects earlier is we load up like a regular page.

55:32

So like a module is something that is intended to be self contained and potentially reusable.

55:37

So you might have a module with, you know, block of text in it or a module with a gallery or like a list of items or something.

55:47

And so sometimes that module is going to be just specific to one page, you can just be set right on the page.

55:53

It doesn't ever need to be reused, but other times you might want to reuse that module on, you know, many different pages.

56:05

So the way bright spot works is a field can be embedded or not embedded, you can't mix and match in the same field.

56:14

And so if we want to be able to support, well, sometimes I want a module to be embedded, sometimes I want it to be shared.

56:21

We actually have to do some extra special work to accommodate both those use cases.

56:26

So you can see here, we've got sort of two options.

56:27

We can add a shared module, we can add a regular module.

56:30

This would be the embedded module.

56:32

So when I add an embedded module, fiq I can just create it right here inside, you know, I can just type in add my items, questions and answers and then this module will live on this page and not be shared anywhere else.

56:46

But if I want to have a shared module, I need to go find one of the database.

56:52

So I want to put my recipe module here.

56:57

And you can see this is actually still an embedded object just like this is up here.

57:01

But all it does is just point out some shared object.

57:04

So that's kind of how we get this to work for both cases.

57:07

So we always have embedded objects in this content field.

57:09

But then some of them just point off to a shared object.

57:12

And whereas other ones will have the actual fields directly in line.

57:16

So the way we actually support that is with, I guess you need three classes to make this work.

57:23

So you would make an abstract class which has all of your common fields.

57:29

And then you would have your in line placement extend that.

57:35

So this would be the case where I wanted to put the fa key module just right in the, in the form.

57:39

And then the recipe module is the shared one experiments, the shared module interface.

57:46

And then this one would just have shared, it would be saved in the database and you can reference it from multiple places.

57:57

You'll see here that we've got this in line module placement interface.

58:01

So this, this marks all the things that are in line modules.

58:05

And then the shared module marks all the things that are shared modules.

58:12

And then finally, the module placement itself is sort of the root interface for all the modules.

58:19

So basically this field here is a list of module placement is the content type of that field.

58:27

And then let's see, we've got a bunch of different modules that I show up here.

58:34

And then we have the shared module placement which marks all of the non in line module placements in our case.

58:41

There's actually only like one that basically works for everything.

58:45

So you don't need to make like a specific shared module for each of your modules.

58:50

There's just one that works with all of them.

58:52

This is actually a new change.

58:53

So if you have an older project, you'll see a slightly different setup.

58:57

So in our case, we only need to make three classes.

58:59

We got our abstract class which has what it actually means to be recipe modules.

59:03

We need to have a recipe.

59:04

We're gonna have an image to override the image for the recipe.

59:08

And then we'll have our shared version and our in line version.

59:16

So there's our shared version go here actually created this for the last training.

59:22

So you can see here, it's got an internal name, but then it just has all the recipe, you know recipe in our image.

59:27

And the reason for the image is, you know, module might be placed somewhere where the regular image won't fit, not the right shape doesn't look right.

59:34

So you can have always nice to have an option to override for an image.

59:37

So you can have a specific image that's useful for that specific content context.

59:44

And I can also do in line recipe module and I can just put them in right here.

59:51

So I can have a recipe and then pick an image or have a fallback image.

59:57

So with those three classes plus the roofs dog guide work we did earlier.

1:00:00

Now we've got modules.

1:00:02

Recipes can just fit anywhere the modules go OK.

1:00:11

Some more things to think about is what should be embedded versus shared.

1:00:15

We talked a bit about that already for modules specifically, but you might have use cases.

1:00:20

We look at like the lead, for example, image lead was embedded.

1:00:25

The main thing to think about here is embedded means not reusable.

1:00:29

That's basically what it means from our context with data modeling context.

1:00:33

We also want to think about what should be searchable.

1:00:35

So basically that means you, you know, go to C MS search type in something and get it to show up.

1:00:40

That would also work if you had a site search on your front end for users to find your content.

1:00:46

I also want to think about if you need site settings or global settings, if you need singleton, we'll talk about singleton a little later.

1:00:56

So what else, what else do you need to make your future work?

1:01:00

There's loads of annotations, we didn't really cover hardly any of them.

1:01:03

So here's some links to the actual list of like everything that you can possibly do.

1:01:10

There's, you know, we talked about required and what not.

1:01:12

There's also ones that can have a minimum, maximum value.

1:01:15

So like those in fields, we could add like minimum value of zero, for example, kind of a negative time.

1:01:21

So there's a lot of different things there that could be useful for you.

1:01:33

this might be a good time to take a break.

1:01:39

Let's go, let's do the miles simple, take a break.

1:01:45

So Bright Spot uses something called model view view model MVVM as our sort of a paradigm for generating data for the front end.

1:01:58

This gets used both for traditional as well as headless.

1:02:02

And the idea here is we're going to separate our graphical user interface or front end, you know, data code from our business logic or backend logic, which is actually the data model.

1:02:14

And the point here is that the data model is designed for the editors to work with.

1:02:17

And it's also designed for us to be able to query for out of the database.

1:02:21

Whereas the view model is gonna be designed for whatever the front end needs which may or may not be directly correspond to what the data model is.

1:02:32

And so the point here is it just sort of converts, you have data model, you use the view model, then convert it to a format that is useful for your front end.

1:02:42

So that's where you're gonna put all your display logic.

1:02:44

You can think of it as a function that takes a model and gives you a, a view that you can render excuse me.

1:02:54

So in bright spot, the model is any class, but it's often a recordable recordable is the interface that record, which is that main class that D provides and implement.

1:03:05

So basically recordable, anything that's recordable can then be saved to the database.

1:03:11

So typically your model is going to be a data model that came out of your database.

1:03:15

And then the view model is a class which extends the view model class, that's a generically parameterized class.

1:03:22

So this M here is going to be the model.

1:03:24

So this might be a recipe or article or something.

1:03:28

And then with interview model, you'll have access to the model and you're guaranteed that it won't be null.

1:03:33

Because basically, if it was null, then you wouldn't even have generated that V model in the first place.

1:03:39

So you don't have to do any null checks, which is nice.

1:03:43

And finally, the view is on the back end perspective is just an interface that you're gonna implement.

1:03:49

And then it's bound to a specific method of rendering which we talked about that handlebars template annotation that binds it to the handlebars system.

1:03:56

There's also an annotation for binding it to a JSON.

1:03:59

You're doing like an API and then if you're doing graph QL, you actually really don't even need view interfaces.

1:04:06

you can just directly code your view model and we'll talk about that more when we get to graph QL.

1:04:14

let's come to this later.

1:04:15

Let's actually take a look at some code.

1:04:20

So our first thing here is going to be the view model for the recipe this is the module here.

1:04:32

So here we've got our PD model.

1:04:40

So here's a recipe view model.

1:04:43

It extends the model of recipe.

1:04:45

The recipe is our data model or data model.

1:04:47

Just our model, which in this case happens to be a data model.

1:04:50

Data model.

1:04:50

Basically means a model that stays in the database, I guess is the term there.

1:04:54

and then implements recipe model view.

1:04:56

And this is what we wrote earlier with our JSON work in the roots style guide.

1:05:04

And so effectively, what the view model does is just implement that interface.

1:05:07

So here, every method here is overriding a method that was defined in the view interface.

1:05:16

So once you've implemented all of those, then you're done.

1:05:22

And sort of the beauty of this approach is by encapsulating everything into this view interface.

1:05:27

If we ever change the root style guide that will then rebuild the view interface and that will then make our view model fail to compile because it's not, it's no longer satisfying the interface.

1:05:37

Maybe we change the data type cook time is no longer a charge sequence, maybe it's a number now or we add a new field.

1:05:43

Now we're no longer satisfying that interface, we get a compilation error.

1:05:46

And so we can quickly discover that there's something a problem and we can go fix it rather than waiting to deploy to production and then see that like the page doesn't work or something.

1:05:58

So in here, we can talk about the different methods to sort of the standard patterns you'll see in the models.

1:06:05

So in this case, it's like a difficulty, difficulty.

1:06:08

Remember that's an enum and we need to return a string to the front end.

1:06:14

So in our case, we're going to take the difficulty, we're gonna get, turn it to a string, otherwise we're gonna return it all.

1:06:20

Sort of standard practice here is any of these methods.

1:06:23

If they don't have a value, then just return all and then the front end will then handle that gracefully.

1:06:29

Basically javascript is good about dealing with the truth and false values.

1:06:33

And so you don't have to worry too much like on the handle our side of dealing with this.

1:06:37

So we just turn all whenever we don't have a value.

1:06:42

the cook time.

1:06:44

Remember that's a number, we need to convert that into a string.

1:06:46

So we actually move that logic into a private method because we have to call this like four times already different.

1:06:52

We have different cook times.

1:06:53

So we're, we're going to call this multiple times.

1:06:56

So we got an integer from the data model.

1:07:02

But it could be null, it could be less than zero if it's bad data for some reason.

1:07:07

So we're gonna fail, you know, not fail, we're just gonna return null in those cases.

1:07:11

Otherwise we're gonna use this library to generate like a printed, nice, nice printed string for the users to see.

1:07:21

And that requires local so that it will print it in different languages that supported.

1:07:26

So locale actually is going to be a value that's injected into the V model at render time.

1:07:33

So just like we have annotations on the data model side for controlling the U I and how it behaves in the C MS, we also have a separate annotations for controlling injecting values into the view model that come off the request.

1:07:47

In this case, this is going to inject the current locale for the request onto into this view model.

1:07:52

So we can then access it something important to note that don't get confused between view model annotations and data model annotations.

1:08:00

They're basically completely distinct, one will not work in the other context.

1:08:07

So view view model annotations are for injecting values from the request into your view model.

1:08:17

So we got some other times of total time is also using the same helper method.

1:08:22

So rich text needs special handling.

1:08:26

It's there's a raw format that stored in the data model, it needs to be converted and rendered into something that the front can deal with.

1:08:33

So we have this rich text to utility library which has methods to do that work for us.

1:08:39

So there's actually two flavors, there's built in line html and there's build html and these should directly correspond to that in line true in line false in your rich text annotation on the field in your data model.

1:08:53

So if it's in line true, which is the default value, so you might not even see anything at all.

1:08:59

And that's in that slot in the annotation, then you would just build in my html.

1:09:05

Otherwise if it's in line false, you would just build html.

1:09:10

And the main distinction between these is that build html will create paragraph tags, whereas M I html will separate things with break tags.

1:09:18

That's sort of the main difference between these two.

1:09:24

And it's a little complicated.

1:09:25

So rather than just passing in, you know, get title directly here, we actually pass in the model and a method reference for how to get the rich text field out of the model.

1:09:37

The reason for that is there's sort of a database implication here where there might be rich text elements.

1:09:44

We have an image rich text element say in this field, which means that we need to then go clear the database to get that image out of it.

1:09:50

And so if we just call get title directly, we kind of lose that connection to the database.

1:09:55

So by doing it this way, we kind of keep everything in sync so that it has the data model, data model knows how to resolve additional references out of the data out of the database.

1:10:05

So that's where we have to pass it in.

1:10:08

And the final bit here is telling bright spot if you find a rich text element, so we find we find an image here, but maybe in the ingredients, we find an image text element.

1:10:21

How do I then render that?

1:10:22

So this is telling bright spot given a rich text element, render out a view model for it basically to this great view, look at this one here.

1:10:36

So create views is a method and create view is another one that's provided by view model class.

1:10:41

And this allows you to then basically create additional view models.

1:10:44

So maybe a better name would have been create view model rather than create view.

1:10:48

And the way it works is you pass in the view interface or view model class that you want to be rendering and then you pass in the model and then it will go find the view model that go that corresponds to that combination and it will then render it out for you.

1:11:05

And if it doesn't find a V model, it'll just return it all.

1:11:09

And then the difference here is create views.

1:11:11

We accept either a single object or a collection of objects and it will, if it's a collection, it will render out a view for each object in the collection.

1:11:21

Whereas create view doesn't only takes one object and we'll just render out one view.

1:11:25

So this returns an interval this returns just a value.

1:11:33

So that's basically it for view models.

1:11:35

So basically the main things here is use, create views to create additional view models using the risk text utilities as necessary.

1:11:42

And then just general being aware of like no values and make sure you just kind of render out things to satisfy the api contract that was drawn up in the Roots style guide.

1:11:54

Mhm.

1:12:00

So let's discuss entry points.

1:12:02

So you know, here, you know, we're saying we need to go find an interview model.

1:12:07

In this case, we know, you know, it's gonna, it's gonna satisfy this field interface.

1:12:11

So go find something for the initial start of the request.

1:12:16

So I type in Bright spot.com and I go there, we need to have some kind of starting point to say find the view model for this, you know, home page and something so that something is going to be page entry view.

1:12:31

So this is the standard entry point for traditional rendering stack.

1:12:38

So basically when you type in URL and we're using the traditional rendering stack, Bry will then find the data model based on the URL.

1:12:46

And then we'll go try to find a new model that implements page entry view and we use that to then render out the page.

1:12:55

There's also something called preview, page view which is specifically for preview.

1:13:01

So you would never use this on the front end for the general public to be viewing your website.

1:13:05

But in the C MS, the editors have a preview p there obviously.

1:13:09

So this, this is what would actually be used.

1:13:12

And if you don't have a preview page, you will then look for a page entry view.

1:13:15

So that's sort of a fall back there.

1:13:19

So we're actually gonna need to write a preview view model for our recipe because so I go to recipe now.

1:13:33

We wrote that Recipe View mo but I'm not getting the go back to the site here.

1:13:39

Oh, oops, that's one thing, you know, you shouldn't be publishing when you're in global, you should always be in a site.

1:13:44

So I made a mistake there.

1:13:45

So if I, I'll just choose a different recipe at this, like that concert is basically not owned by any sites.

1:13:52

So it's kind of orphaned.

1:13:56

Yeah, I go to this one.

1:13:58

So see, I'm not getting a preview icon here even though I have a recipe view model written.

1:14:04

And the reason for that is there's no connection between that recipe view model and the preview entry view or the page entry view, which is the fallback.

1:14:13

So because it can't find a view model, it doesn't have any preview to render here.

1:14:16

So we actually need to write a view model to satisfy the preview use case.

1:14:26

So what we did, we added this recipe freeview model.

1:14:30

And this again is gonna key back into that underscore wrapper piece we talked about earlier.

1:14:36

So the way this works is preview page view is what all those wrapped views to our modules.

1:14:44

Everything that's not a stand alone page is gonna then be wrapped up into this view.

1:14:49

This you got generated automatically for us and it has this yeah, this field here.

1:14:56

So G main is where all of the things we're going to preview fit into this.

1:15:00

And you can see here, here's all the, basically all the things that are not stand alone pages in this project.

1:15:06

And you'll note here there is a recipe module view is one of them, but we've also got all of our different things like different modules.

1:15:12

We talked about container modules earlier.

1:15:14

So those should show up in here somewhere, your phone call and container view and so on.

1:15:24

So we need to write a view model that implements the preview page view and adds the preview entry view and takes our recipe as the model so that everything gets tied together.

1:15:37

So the main piece here is this method here at main, we're passing in our model which is the recipe and we're going to render out a view model for the preview page main field.

1:15:46

And if I go into that, that's where the list we're just looking at.

1:15:49

So recipe module view satisfies that scroll down here.

1:15:59

Why I do that?

1:16:01

It's too long.

1:16:02

Anyway, it's in here.

1:16:03

It's on this list.

1:16:04

And then if we go, what is it you?

1:16:12

So we click into that.

1:16:13

Now we go here.

1:16:14

We have a recipe D model so that will then tie everything together.

1:16:18

So this, whenever this renders it will pass in the recipe here, I pass in preview page main field and I will be able to find that recipe module, the model that we just wrote and it will actually tie everything together and render out a preview for us.

1:16:38

And the preview interview itself is just a marker interface.

1:16:40

I don't have to implement anything.

1:16:42

Just adding it itself is enough for the system to find it.

1:16:46

So with that in place, we should then see that preview icon show up much should be able to then preview this new recipe.

1:17:00

So this is the recipe that's being rendered inside the preview page template.

1:17:13

One other thing I wanted to show you is so the preview entry view, there's actually only a few implementations of that.

1:17:21

And the reason why is because 90% of the things that we're gonna want to preview are modules and all the modules would implement shared modules, we actually can cover like 90% or more of our use cases.

1:17:33

But this is just one view model.

1:17:35

So if you're writing a module, you'll get preview for free it's only if you're doing something custom like this recipe, which it is not a page but not a module, but you need to do a preview of view model.

1:17:44

So it's probably not very common.

1:17:46

You actually need this.

1:17:47

, So, but I just wanted to kind of show it to you so you can understand the, how the system all fits together.

1:18:02

And if you do have a use case where you need it, it's good to know how it works.

1:18:12

I'm told this one has a hand raise.

1:18:14

If you have a hand, if you have a question, just go put it in the Q and A.

1:18:18

I don't, I'm not paying attention to the hands being raised, just put a question in the Q and A and I'll see it.

1:18:28

So here we've got now our recipe page, a recipe article, page remodel.

1:18:41

So I'm not going to go into this to basically we have page interview on this one because recipes are stand on pages.

1:18:46

So we need them to work, not just in preview but throughout the entire Bright Spot ecosystem.

1:18:54

So we've got a recipe article as the data model as the model and then preview page, sorry, implementing page entry review so that bright spot can find it when you go to a recipe article URL.

1:19:09

And then we have to implement our actual recipe article page.

1:19:12

So we actually know what to supply out.

1:19:16

I'm not gonna go into any detail about any of that.

1:19:17

So it's all basically the same as what we've already looked at.

1:19:20

The one thing I do wanna mention is sort of the just like we talked about.

1:19:29

Yeah, so our recipe article, page view, remember that's extending creative page, creative work, page view that extends content, page view and that extends page view and page view is kind of where all the standard page stuff, like all the stuff for your head element, like the title and meta tags and stuff all live than that.

1:19:47

And so just to save effort, a lot of those views have a corresponding abstract view model to just kind of fill in the bits for each of those views.

1:19:56

So if you're going to write a page, but I don't need to write an entire all those methods all over again, you can just extend abstract content, page view model or abstract page view model as the case may be based on how your view fits into that hierarchy.

1:20:13

So you don't have to basically get all this logic for free.

1:20:17

Just keep that in mind when you're doing your pages, you probably only need to have, you know, a few 100 lines tops for your specific content type and all the other stuff can come from these abstract view models.

1:20:35

So our final piece here again, we'll go over this pretty quickly is the module of your model.

1:20:47

So remember on our, our, our recipe module, we have the recipe, but we also have the override image so that if the image that goes with the recipe doesn't fit in this context, they can put a different image there instead.

1:21:03

Because of that, we actually have to kind of duplicate the the recipe module view model that we just, yeah, the recipe view model, we just wrote and copy that in here and have it work with recipe module rather than recipe.

1:21:23

And the reason for that is we need to have this extra logic for getting the fallback image if it didn't have that requirement.

1:21:34

And we just had, you know, the the recipe model just wraps a recipe.

1:21:38

then we actually could implement what's called model wrapper on our view model.

1:21:45

Sorry on our, we could do why you describe it rather than show it, show it right and describe it.

1:21:51

So I could actually do this model wrapper.

1:21:57

And then I'd have to implement the method from that.

1:22:06

What this does is anything implements model wrapper.

1:22:09

When bright spot you, you know, you call creates and you pass in your data model.

1:22:14

If that thing implements model wrapper, then it will call this unwrapped method before looking for the view model.

1:22:22

So in this case, it would return a recipe and then it can find the recipe module, the model that we've already written.

1:22:28

But if we did that, then we would lose the ability to do this override image.

1:22:33

So in our case, we can't do that.

1:22:34

But if you do have a case where you just have something that's wrapping something else, it doesn't have any additional like needs on the rendering side, you can implement model wrapper and just return like the underlying thing which actually has the V model attached to it.

1:22:49

That's a little bit of the savings when that actually works for you.

1:22:52

In our case though, since we have that override, we can't throw away our recipe module and just go direct to the recipe because then we wouldn't have the ability to do an override.

1:23:00

So here we're going to we have a recipe module as the model, but basically all of our data is going to come off of the recipe itself.

1:23:09

So we're gonna use I guess you call it like the V model life cycle has when the V model first gets created, it's a couple of methods that will execute before it actually will then render it.

1:23:24

In our case, one of the methods is called should create and lets us to do some sanity checks and basically say, oh, we shouldn't render this V model, it doesn't make sense to.

1:23:32

And so in which case, it will just return no as if like the new V model didn't even exist.

1:23:38

In our case, we're gonna check to see if the recipe is not.

1:23:40

No, again, it's required, but we're gonna do a no check just to be extra safe here.

1:23:48

And so only if the recipe is non knowable, then the rest of this few might execute.

1:23:53

And then the rest of it is basically just a copy of the one we just looked at.

1:23:55

But instead of calling model, which in this case would be the recipe module, we're gonna call recipe to get all the data off the recipe.

1:24:02

And then the only place we use the model is for the image because this is where the Fallback logic exists for the override image or the fall back from the recipe.

1:24:16

OK, I think that is everything.

1:24:23

Here's a link to just all the different annotations you can use for view models.

0:06

So we move on now to some advanced data modeling.

0:12

So we have some new requirements.

0:16

Editors need to be able to search for recipes by difficulty recipe tags the various times, We wanna be able to type a tag name like appetizers and then it will show all the recipes tagged with that.

0:34

And then we wanna be able to sort the recipes by difficulty.

0:37

We had like the easy, medium, difficult or whatever they were, we wanna be able to sort on those, you know, so the easy will be first and the medium and the difficult.

0:48

So we're gonna need some new techniques to satisfy all of this.

0:54

Just go ahead and get the code going there.

0:59

So somebody is gonna be easy, something can be difficult.

1:02

So we go to the data model here.

1:07

And we'd already put the index on the title.

1:10

We may have already put the index on the difficulty as well.

1:14

But remember here, difficulty is an genome.

1:16

So under the hood, this is actually gonna be a string value which is gonna be one of these three constructor names.

1:23

So we can't really start on that because they aren't necessarily going to be alphabetized in the right order expert would become before intermediate, that wouldn't work for us.

1:34

So we need an index but it needs to be the numeric value here of the 12 or three rather than the string value here.

1:43

So there is a tool for that.

1:46

it's called index method.

1:49

So basically, you think you might think of index as being only on fields, but we can actually also apply it to methods.

1:55

We have some restrictions though.

1:58

they can't accept any parameters which makes sense when you think about it because how would we know what parameter to pass in this has to execute it, you know, save time basically.

2:09

So we can actually write the data into the database for the index.

2:13

And then there's a restriction just for like a sanity check to has to start with get is or has.

2:17

So it has to be kind of like a Getter pattern method.

2:22

And so what happens is whenever we save the record, bright spot obviously will find all the index fields and and save those as indexes.

2:28

But it will also find all the index methods and then execute each one which it can do because they don't take any parameters.

2:34

And then whenever value gets returned, it will just store that as if there had been a field that just contained that value, so they're very convenient.

2:43

And the next method is actually will display as a field in the C MS as a read only field.

2:47

So, if you don't want that, you need to hide them with the hidden annotation, it might be useful to show them depending on what they do.

2:54

So we'll go ahead and take a look at one of these.

2:56

So we got the difficulty level.

2:58

So we're gonna get the difficulty, we're gonna get the code off of the difficulty, otherwise a return mole.

3:04

And then here we said it's gonna be index hidden because we already have the difficulty visible in the C MS.

3:09

There's no need to show it twice and then we also want it to be sort sort over here.

3:14

specifically applies to the C MS search.

3:17

So if you want to sort difficulty on like the front end search, that's a different topic sort of outside the skill of this training.

3:24

But you would have to do extra work for, to make that possible.

3:29

This only applies to the semester.

3:31

So editors will be able to sort by difficulty level with this one line.

3:37

And then here we're returning null.

3:39

So basically index, if you don't have an index method, you can always return null and it just means that you won't do any write anything into the database for that.

3:49

we wouldn't want to write zero for example, because then it would just put a zero in the database.

3:53

We actually want to put.

3:54

No.

3:55

So there's nothing that's written at all.

3:58

We've got some other indexes here.

4:02

We've indexed the title plain text.

4:05

We're gonna use that later for the graphical API because right now this index actually probably we should probably just remove this.

4:09

This is gonna index the rich text value which might have markup in it for the various, you know formats that are allowed bullet tag on the line and what not.

4:17

Whereas this plain text value here is going to be just the actual text itself, you know, asking characters, I guess it could be un code characters, but it won't have any formatting.

4:29

So again, we're indexing that we're gonna hide, hide it because we already have the title visible as a field.

4:34

We don't need to show it again.

4:38

For the recipe tag names.

4:40

This is a bit more complicated.

4:42

So what we're doing here is we need to go to every recipe that we, every tag that we have recipe tag that's associated and we need to then get the name off of that and store that in our index.

4:53

So that basically involves a reference result because we actually have to go query for the recipe tag, pull that out of the database, get the name off of it and then save that And so what sort of got you here with index methods is sort of by default, you know, when you call, you know, up here, if we call, you know, just like get image or something, it's the rice bottle, I guess will automatically query the database for that image and give it to us.

5:23

We don't have to do anything special to make that work.

5:25

But index methods run in a different context.

5:28

And so for performance reasons, I guess they won't necessarily resolve those references automatically.

5:37

So we have to force it because otherwise we would just get nothing back.

5:41

We would it would resolve the reference.

5:42

We basically get a no value back for the recipe or an empty recipe, that recipe tag back with no data fields, it was actually resolved and then we would just have a value here.

5:53

We would never index anything.

5:55

So this utility here allows us to force that to be resolved.

6:00

So this case, we pass in the thing we want to then create resolve the reference from and then the method reference to actually get the data.

6:10

So in this case, it's going to be the getter for the recipe tag.

6:12

So with this in place, it'll then run, it'll execute the getter in the context that will actually resolve the references.

6:22

And then, so once we actually resolve the references, then we can then stream over all of the recipe tags, get the name converted to plain text because again, the name was rich text.

6:32

And that's why I never mentioned that.

6:34

But as the best practice, any string value that's gonna display on the front end should be rich text.

6:38

So the editors have control over how it so formatted.

6:43

You might have specific reasons why you wouldn't want to do that.

6:44

But as the best just general practice, you would make all those skills of rich text.

6:50

So in our case, this is for a search.

6:52

So the editor, you know, the users not gonna type in any formatting, they just want to find appetizer recipe tag.

6:58

And so we'll convert that to plain text and then collect all those to set.

7:05

And the main here was a cassette versus a list here is the set list could store duplicates, but actually the we won't store duplicates.

7:11

I don't think in the database itself.

7:12

So they would really be equivalent.

7:16

in this specific case.

7:23

We've also got our times indexed.

7:27

So the cook time, interact, inactive, prep time and the prep time are all indexed.

7:33

But the override, we cannot index the field here because there might, it might not have a value when we're using the fallback.

7:39

So we actually have to index the total time instead.

7:44

So this method already existed, we now we just added index to it to make that work.

7:51

I think that was all of them.

7:59

Yeah.

7:59

So then oh so I probably made an edit somewhere when I was doing something.

8:06

Yeah, I added the model rapper.

8:09

So yeah, there's the check style job that runs task that runs as part of the bill and just finds, you know, common mistakes and things, you might make extra imports and things.

8:18

And so I got flagged by that.

8:20

So let's go fix that real quick.

8:24

Yeah.

8:33

Mhm Yeah, I took that out.

8:43

So let's spell that again.

8:47

So by adding all of those indexes, going forward anytime an editor says a recipe, they will all get populated but for any existing recipes, they won't be populated until you reate them.

9:02

So something to keep in mind when you add new indexes, typically you're gonna need to do some kind of re index job.

9:09

Once you deploy that code to populate all the indexes for the existing content and then going forward all new content and edited content will then also have the indexes populated.

9:25

So I wanna show you kind of how you would do that with a tool for that.

9:33

And I'm gonna use that recipe that we saved, accidentally saved in global as an example because I know that one doesn't have these, these indexes yet because I created it when we hadn't written that code.

9:47

So C MS has been a little slow now.

9:53

So I'm gonna switch into the global, the global is absence of the site.

9:57

So now we don't have a site.

9:59

So they go to this recipe here.

10:02

We can go look at that raw data here and you can see at the bottom of all the indexes and you'll note here that we don't have any of that stuff index except for the title.

10:16

So what I'm gonna do is there's this developer area here.

10:20

And you probably won't see this, you might see it on this one environment and local here because I've turned it on.

10:26

But in a Q A environment, you probably need to create a special role that will allow you to access that.

10:36

Because by default, it's locked down, you can kind of do some, you can do some damage in there.

10:39

So you don't want to just have anybody with access to that.

10:44

So in our case, this local environment is has it enabled.

10:48

So you don't have to do anything special here, but normally you'd have to create a developer role.

10:53

You would have the all developer permissions by default, it would have none and then you would add that role to your user like that and save it.

11:05

So that then let's get this to show up on like a Q A environment.

11:09

In our case, I don't have, I don't need it because I sort of turned it on specifically for this example environment anyway.

11:15

So under the developer area.

11:16

There is a database bulk operations.

11:22

How this works is I can, do an index job here and I can pick recipe and I just need one writer.

11:32

We only have a handful of recipes and start that.

11:35

And then I could go over to this task page and watch that, index job complete.

11:41

So in the case, it indexed eight recipes.

11:45

And now if I come back over here refresh, we can see that we've got some more indexes.

11:50

So we've got our difficulty level index.

11:52

Now, the inactive prep, time and time are indexed, the difficulty is indexed.

11:57

So basically, we've, we've populated all these indexes for the existing content and then going forward any time I edit or say this, since we have all that indexing code in place, now it'll just fill it all in automatically.

12:08

For me.

12:09

I had to do this one time step to bring up all the old data up to the new code.

12:19

There's one more piece we're gonna need here though.

12:21

And that is because now we've introduced a data dependency between recipes and recipe tags because of this index into tag names.

12:34

So what's going to happen here is if someone was to rename a tag, a recipe tag, change the name of it.

12:39

Now, all of our indexes are stale, we would need to recalculate them.

12:44

So obviously, we could do that by manually by going in and picking the, you know, the database bulk operations we just did.

12:51

Oh, I forgot one piece of that actually.

12:54

Sorry about that.

12:56

There's sort of two pieces here.

12:58

Oh, still loading.

13:01

I have to wait for this line to clear.

13:03

Now I can load it.

13:06

There were two options there, there was index and then there was copy.

13:10

So the index operation only applies to the mysql database, which is one of the two databases that bright spot runs on in a standard setup.

13:21

And so that database is where all of the actual data JSON data is stored for each of your data content types, but it doesn't support full tech search.

13:30

So we have another database SOLR which is actually runs the full tech search capabilities.

13:38

So actually this D MS search typically goes to SOLR because you know, I can type in text here and have a full text search experience.

13:44

And on the front end, the site search page would also use SOLR.

13:50

So index here only applies to my SQL, which means I also need to do a copy from my sequel to Solar.

14:03

I I only need one writer and then just delete before copy.

14:06

You may or may not want to click that basically that will clear out SOLR for this content type before then copying.

14:13

So if I click that there would be some amount of time where we had no recipes at all until we could then populate them again.

14:18

And obviously, if you clicked all, then you would have no data in SOLR until you had, the index job completed.

14:27

So only in very rare cases, would you ever wanna check this box?

14:30

I'm not gonna check it now.

14:33

So this is then going to do all the copying from sequel to SOLR, which will then populate the SOLR indexes.

14:38

So that full text search also still works with all of our new indexes.

14:41

So there's sort of 2 to 2 part process there.

14:44

So don't forget to do.

14:47

So yeah, with that in place now, we can move back to the, the data dependency we have here between recipe and recipe tag where if a recipe tag gets changed, none of our recipe indexes are stale.

15:02

So the way we're gonna solve this is we're gonna use the save life cycle, which is basically a bunch of different methods that will execute every time you save something and they allow you to hook in and inject logic into that process.

15:20

So the first one that will execute is called before save.

15:25

It's a bit of a misnomer.

15:28

Basically anytime an editor presses a key and when they're working bright spot will then save their progress.

15:35

And you kind of saw it earlier when I had that yellow, you know, you've made changes to this.

15:39

You know, content message in the C MS.

15:42

So that is what save is considered here.

15:44

It's sort of a progress save, not an actual, like I push to publish save.

15:48

So this runs all the time and it's useful for just doing really quick, small, you know, like setting something because something else is that kind of thing.

15:57

You would never want to put any expensive logic in here.

15:59

You would never want to make API calls or anything like that because it'll just make the C MS really slow.

16:05

So that one runs all the time, then the rest of these only run when you actually click publish.

16:09

So the first one is on validate, which lets you do custom validation.

16:14

So we already have a lot of annotations that let us do validation, like the required annotation, there's a minimum annotation.

16:19

But if you have some requirement that doesn't, doesn't fit in an annotation, usually it would be something like multiple fields being combined.

16:26

You know, if something is set, then something else can't be set or something like that.

16:33

This allows you to put that logic in and the way it works is if you have an error to want to report, there's a method called add error on the state, we didn't really talk about state yet.

16:44

State is basically the data that backs a record class.

16:49

So record is the standard class everyone's gonna extend for your data, your content types state sits behind that and actually stores all the data in the database and then pull it back out.

17:00

So it's basically just you can think of it as like a dean object just like a map of, you know, keys to values.

17:08

But it also has a bunch of utility methods including this add error.

17:11

So you can then add an error to a field and then it will show up in the C MS with a red error on that field saying, you know, you got a problem here.

17:20

So I don't think I have a specific example of how to use it.

17:23

It should be covered in our documentation.

17:25

And you get to state by calling, get state from your record.

17:29

That's just sort of a standard method there.

17:34

So then after on validate runs after validate will run, it always runs regardless of whether there was a validation error.

17:43

It's a relatively new method.

17:44

I don't have much to say about it.

17:47

The main method here that you really want to put up most of your logic and is called before commit.

17:50

And you can think of that as really being like before database or saving, publishing to the database.

17:57

This will only execute if on validate succeeds it all the validation.

18:01

So there's no required fields missing or anything else.

18:06

And then this is where you can actually put some more important logic.

18:11

And then on duplicate runs only if there's like a uniqueness constraint.

18:15

So on index, you can make an index unique.

18:18

In which case, if there's a already a value for that index, then you can use on duplicate to, you know, avoid the duplicate by adding a dash one on the end or something like that.

18:29

And then finally, if the save succeeded, then after save will execute.

18:33

So we're going to use before commit and after save to, to handle our data dependency here.

18:39

So in recipe tag, so anytime recipe tag gets saved, this log will execute.

18:45

And so basically, we're looking for the case where an editor came here and changed the name.

18:50

If the name change, then we need to go re index all the recipes that are associated to this recipe tag, that's the requirement.

18:57

So the first step here is we need to query for the previous recipe ver version of this recipe tag to compare the name.

19:03

So here's our first query.

19:08

So query is always from something.

19:10

So in this case, we're gonna query from all from the entire database.

19:14

And we're going to query by ID.

19:16

So where the id equals a value, this is like a parameter substitution.

19:21

The question mark will then be substituted with a parameter that we passed in.

19:24

In our case, we're going to pass in the recipe tag itself So we're gonna basically go find the thing in the database that has the same ID as this recipe tag.

19:33

The important thing here to notice is no cash.

19:35

So by default, breasts spott heavily caches things to improve performance.

19:40

And so what will happen is I don't have this line here but take that out.

19:47

Right.

19:47

So we'll probably pull this exact same recipe take that we're looking at right here right out right out of the cache and it will be exactly the same as the one we we're saving.

19:56

And so when we compare the names, of course, they're gonna be the same.

19:59

So we need to have no cash here to tell it to actually go back to the database and pull out the original version of it so we can compare them.

20:08

And then we're just gonna get the first thing because recording by ID, it's unique.

20:12

We shouldn't have any other values.

20:16

So this is sort of being extra paranoid.

20:21

Typically you could just, if it's not, no.

20:24

it is possible to change the type of something.

20:27

It's rare but it is possible that editors like maybe they have three different article types and they want to be able to switch between them.

20:34

you have to do some work to support that, but if you do support that, then it's possible that the content type could have changed.

20:39

So, in rare cases, it's possible that you query the database that comes out, but it's not a recipe tag, it's something else.

20:45

And so that would be the case where the editor just changed the, the type and we're saving at that point.

20:52

Anyway, so we're gonna run and just do no check.

20:54

We're gonna check to see it's the right content type return.

20:56

If it's not, if it's a different content type, then obviously it can't be a duplicate name or, or we can name kind of change.

21:02

Basically, it's effectively a new recipe tag.

21:06

So now we're gonna get our previous version cast that and then we compare the names and the names are not equal.

21:15

You might think, OK, well, here we could go and re index the recipes, but we're in before commit, we haven't actually saved yet.

21:22

So it's possible the save will fail in which case we don't want to have re indexed yet.

21:27

So what we want to do is just save a note to tell us to go re index later.

21:32

And so this, this is that get state method I was telling you about.

21:36

So in addition to that add error method, there is also an extras map which just lets you put key value pairs that don't get saved in the database but do persist throughout the safe process.

21:48

Excuse me.

21:49

So, we're gonna store the fact that the name was modified and then later on, we can then check that.

21:59

So we'll just store that and then we'll let the save continue and then in the after save, we then do that logic.

22:05

And then the reason why we have to do this logic here, we can't move all this logic.

22:09

And after save, because if we're doing an after save, then the previous version will have been overwritten already.

22:15

And so we can't, we wouldn't be able to compare the name there either.

22:17

So we kind of have to split it up into this two part process.

22:20

So we, and before commit, we query for the previous version, we save whether we need to do more work.

22:25

And then after save, we actually do more work if we need to.

22:28

So here we're going to check if we had a true value in the extras and if we do, then we're gonna kick off a job to go re index the recipes.

22:41

So there's a utility that already handles.

22:43

This is basically we'll save a job database and then something will come, task will come along, look for those jobs and re index them just asynchronously.

22:52

So we don't have to hold anything up right now.

22:55

We can just let it happen in the background.

22:57

And so here's another query.

22:58

So query from all again, in this case, we're querying on the recipe, tags, field index.

23:08

Another way you could have written this query.

23:23

So it could have been like that.

23:31

The reason we don't do this is because this query will query over the entire database.

23:37

This query will only query over visible content.

23:40

So if a recipe has been in draft status or it's been archived, this query will not find it.

23:47

And so we might get recipes that don't get re indexed.

23:50

So we query from all, then we will get everything except we have to then fully qualify our index names because basically, it, this will fully qualify automatically for us because it knows we're recording a recipe.

24:01

Whereas this case, it doesn't know the recording a recipe.

24:03

So we actually have to add the class name on the front of the index name.

24:11

So you would use this kind of query for anything that's gonna display on the front end because you only want to show visible content there.

24:18

You would use this kind of query when you're doing like tasks and data, data type stuff where you want to make sure you get all the visible, all the stuff regardless of whether it's visible or not.

24:29

And then, so basically, this recalculate method by query takes a query and it takes the name of the thing you want to re index.

24:35

So this will be that same.

24:37

This is the recipe name.

24:38

This would be the tags field.

24:40

This is the names field.

24:42

If we go back to the recipe, this to show the distinction between those two.

24:45

So this is the recipe tags field.

24:48

This is where the editors select tags and then the recipe names field is really the method in next method that we wrote is this here and we've saved both of those into a static string just for convenience.

25:02

So these are just the names of the actual indexes.

25:05

So index methods, the name is the name of the index method, including the get at the beginning and then the tag or sorry.

25:12

The field would just be the field name as it is in the Java class.

25:19

So with all that in place now, whenever we save a recipe tag and rename it, then all of our theorized na name indexes on all of our different recipes will then get updated as well.

25:33

And we've also done the same thing for after delete.

25:37

So if we delete a recipe tag, we want then remove it from the name index.

25:45

Another gotcha here is this is an actual database delete.

25:50

Typically an editor will just archive something which actually counts as a save rather than a delete.

25:56

And so if you wanted to cover the case where something was archived but not deleted, you would actually need to put a special case up on this half, you know, checking for the name changed or it went from published to archived.

26:10

And so I'll leave that as an exercise to the reader to add that logic.

26:14

But that would be something you would have to put in if you actually cared about that use case after delete only counts.

26:18

If you archive and then delete would, then the delete, there would, would be what this executes, what triggers this to execute.

26:36

Let me show an example of that.

26:42

So here's our, I guess I didn't tag this with them.

26:49

Let me add a recipe tag to this example and then we can then rename it.

26:56

Oh, so this is a BS A and Global actually gonna move it into the Inspire conference site.

27:03

So I wanna go there.

27:04

Now, I've got it.

27:07

So they had a recipe tag here.

27:12

Publish that.

27:14

No, if I show our data here, we can see we've got the recipe tag names as vegetable side.

27:23

So then if I go to the vegetable side, maybe I just rename that to vegetables, what's gonna happen is it's gonna, that's gonna in the background, start up a task.

27:39

It's gonna run every minute.

27:40

So it might not run until 305.

27:43

But then eventually here we'll see this actually already ran.

27:47

So the recipes already ran and updated our recipe, this index method to then be the new name.

27:54

So everything kind of seems to be working.

27:57

So that's sort of the process.

27:58

Anytime you've got a data dependency across multiple content types, you want to do some very, something very similar to this, this approach to keep everything in sync.

28:11

So here's a delete life cycle we talked about after delete is also before delete, which runs before you delete it.

28:17

And again, these only run for actual delete, not for archive got some more documentation on the full explanation of all those different methods.

28:28

Excuse me.

28:32

So we got some more advanced data modeling here.

28:40

So Java does not support multiple inheritance for various good reasons.

28:48

which basically boils down to our, for us being that interfaces do not support fields.

28:54

So sometimes you might want to share a field with multiple content types, but you don't want them all to extend, you know, abstract whatever to get that field, which would be the how it would work in just plain Java.

29:07

So what we have in in bright spot is something called modification which enables us to share fields among all classes that implement an interface or extend some other class.

29:20

So how it works is we have an abstract class which extends record called modification, but it's not a regular content type that you would normally expect like we might have an abstract article that extends record, but that's would be handled very differently from modification.

29:34

Modification gets special handling.

29:36

So modification is a parameterized class.

29:38

This T parameter here is what you're gonna modify.

29:41

So this could be anything it could be recipe article, it could be some abstract class, it could be an interface and then basically anything that instance of that thing is true would then also get whatever things you have fields and methods that you put into that modification class.

30:01

But it's still a separate class.

30:02

So we're not doing bytecode manipulation here, like inject those fields into some other class.

30:06

It's actually a separate class from java perspective.

30:09

It's just another class.

30:11

And so it's only in the dri ecosystem where we have this way to get from the main class like article to the modification.

30:20

And then back again.

30:24

So then we have a common pattern of where you might have a requirement to be able to search, you know, multiple things with the same index.

30:38

So in bright spot, you know, we saw indexes or either fields or methods.

30:44

And so if you want to have multiple content types, all share the same index and they all have to share the same interface or base class in order to have that.

30:55

And then you might also want to supply that index from the field or maybe you want to supply it with some business logic using the method somewhere.

31:04

So you kind of have this sort of complex pattern here coming up where you have an index and you might have also have a field and you want to be able to connect everything together.

31:13

So we come up with a pattern that sort of captures this use case And so I wanted to include it here.

31:18

So you can see it because you'll see various other classes in your bright spot, like starter kit when you first start a project that follow the same pattern.

31:25

So it's important that you understand how all the different pieces.

31:29

So let me just I used to look at the code and just talk about it in an abstract way.

31:41

So the first part of it is we have an interface that encapsulates the idea that you can associate recipes to something.

31:49

So anything that implements has recipes and then be associated to some recipes.

31:55

So it's pretty straightforward.

32:00

And then the main purpose of this is we want to have an index.

32:04

So we want to be able to index this method is effectively what that means I can't put an index method annotation on it right here.

32:12

But what instead I'm gonna do is I'm gonna create a modification.

32:15

So here's a my data, my has data class extends modification of this interface that we just looked at.

32:24

And then what this means is that this index method will be in like is attached to anything that implements has recipes.

32:35

So our index method we're going to have index, this filter will here again applies to the C MS search.

32:40

It basically means it will show up as a filter in that left hand filter area in the C MS search.

32:47

and then we're gonna hide it because we don't want to show the recipes.

32:51

Well, if you want to show them, we'll show them somewhere else.

32:56

So,, we using that same utility to resolve the references and then what this does is we call the original object that just returns whatever this is, whatever implements has recipes is what will be returned here.

33:09

And then we use the getter that's defined in our interface to actually get the recipes to then index.

33:16

So by doing this, we basically made an index method that can be shared among any, any number of kinds of types that don't have any other relationship other than just the implement has recipes.

33:30

So that is a convenience.

33:31

, oftentimes you would want to supply, you know, let the editors just pick the recipes directly.

33:38

And so we have this has recipes with field which extends the has recipes and supplies that method from has recipes with data from a field.

33:50

So we've actually got AAA second modification here.

33:54

Modifying has recipes with field and it just has a recipe field inside it.

33:58

So the editors can actually pick recipes.

34:01

But no, we have not indexed this field because the index is actually going to live over here in the index method.

34:07

So this is just a field just for the editors to pick.

34:10

And then finally, we wire all that together here by getting the recipes out of our modification off of that field.

34:20

And so this method here is sort of that as method I was talking about earlier.

34:24

So this a method actually comes from the recordable class which I talked about.

34:32

That's all of your stuff is going to be extending recordable.

34:36

And a note here, we've got recordable on our interfaces.

34:41

This one has recordable via has recipes.

34:43

So everything kind of gets tied into that ecosystem via the recordable interface.

34:48

And so here we can call as we can pass in the class that we want to be able to switch to.

34:53

So if we start out here, we might be on, you know, we we this on video or something for like this recipe about videos, video about recipes.

35:03

So maybe we have a video video here, we can call as we can pass in the modification we want to get to and this will return that linked modification for that specific video.

35:14

So here we can call that method and then we can get the recipes out of it which then gets supplied into has recipes.

35:20

So there's sort of four classes that are involved.

35:22

one for the association, one for the index, one for the association with the field and one for the actual field.

35:33

Yeah, this is sort of like the Cadillac model.

35:35

You only will need this in certain situations.

35:37

So not every index needs this treatment, it's going to be based on your requirements.

35:42

So typically you would do this approach for like maybe you have some kind of taxonomy and you want to have 15 different content types that always share the same taxonomy.

35:51

This might be a good approach for that.

35:53

Whereas if you just have one content type that has an index, you don't need to do all this extra work.

36:02

So here's just a description of what we just talked about And you'll note here that we have several other patterns that several use cases that follow those patterns.

36:14

Like these are just the regular tags, not the recipe tags.

36:19

We can see here, we've got the hashtags field, they have the data.

36:23

So the sort of pattern used several times section is another one section, secondary section, you'll see this a lot.

36:32

So it's important to understand it and you know, when it's important to be used.

36:38

So the next step we actually need to then use it.

36:44

So in our case, we're going to and where is the recipe article we're going to add has recipes to the recipe article.

36:58

And the reason for this is we already have a field.

37:00

We've already got that from earlier.

37:02

So all we need to do is just supply the recipes to this interface and then we'll get that index for free.

37:10

So down here implementing has recipes.

37:16

So when you get the recipe, we have to wrap it up in a list because we have to turn our list.

37:19

We can't just return a single recipe and then it's best practice to never return no, from a list method.

37:26

So we're going to an empty list if we don't have,, a recipe is not set.

37:35

So we're not, actually we're using the has recipes here because we already have the field.

37:40

But you would use has recipes with field.

37:42

Maybe you said you want to attach this to like a video or something and maybe some videos can be associated with recipes.

37:47

And so, so maybe with this in place and you could then query over both articles, recipe articles and videos that match the same recipe.

37:58

That's sort of the use case here.

38:03

So that if we, we load this, yeah, got a recipe article.

38:25

So now if I select the recipe, here we go now to our raw data.

38:38

OK.

38:40

We'll see here that we've got our Yeah, here it is.

38:47

So here's our has recipes, data modification.

38:51

We get recipes and then it's pointing at the recipe that we just selected there.

39:00

And you'll note here, this is not, this is actually an extra little snippet at the beginning of the name.

39:05

And the reason for that it's the best practice is we've got this field, internal name prefix.

39:10

So because this modification to be modifying any number of things, it's possible there's some other index called get recipes and So we have a name clash if that was the case.

39:20

And so by adding this field, internal name predict, it's basically gonna add this snippet under the beginning of all the different names of the fields.

39:30

So if I had a field in here, this would also get added onto the front that becomes the will be called the internal name.

39:37

That's also will show up here if I go down.

39:40

So like headline here, you see this, this is the internal name.

39:45

So this by default is just gonna be the name of the Java field.

39:49

But if we have that internal name prefix, then it will actually be prefixed by some other value.

39:53

So in this case, this is the internal name here.

40:01

It's those two combined that just helps reduce conflicts.

40:08

Got a little bit of time.

40:09

So let me show you how you would do this to,, I guess it would be a youtube video.

40:21

I guess I don't have this class.

40:22

It's not in the project.

40:26

We'll do it the article so I could implement as recipes with field here like that and then to build that.

40:44

But I just, I would get that field for free on regular article, not recipe article.

40:49

And so then, we can also associate regular articles to recipes as well.

41:00

Do you have any questions about any of this?

41:02

No.

41:02

OK.

41:05

So we're gonna move on, I'll just show you what this looks like.

41:07

Now, it could be useful.

41:10

So you can see the internal name details.

41:13

Still waiting for that to deploy your focus darker has gotten slower as, as we moved through this presentation, there we go.

41:55

So they got a regular article.

42:03

So we now have this recipes field so I can add recipes to this article.

42:07

And if I inspect into this, we can see that this comes from has recipes with field data.

42:13

And then the internal name is has recipes dot recipes.

42:19

And again, that becomes from the the term name prefix that has recipes.

42:27

So rather than you would expect this normally just be called recipes.

42:30

But because this exists, it's actually called has recipes dot get recipes.

42:37

And the main thing to note there is that when you're querying, you're using the internal name.

42:41

So you always need to use this value and not this value.

42:47

So often times they're the same, but when they're different, you have to use the internal name.

42:54

OK.

42:55

We will now conclude with graph QL.

43:01

So up till now, we've been looking at handlebars, which is a traditional rendering approach.

43:06

And we've been working with the root style guide.

43:09

All that entire ecosystem is designed for working with handle bars and it's not designed for creating a public API So with an API, it's got to be robust, it's got to be curated, it's got to be maintainable.

43:22

The whole point of building an API is you have some external party that's going to be consuming data from you.

43:28

And typically they're, you know, either just the general public, you have no control over, maybe they're a different department at your company or something.

43:34

, but there's some kind of distance between you and them.

43:39

And so you need to basically build something that you can maintain over time.

43:43

And if you need to make changes, you can't just go call him up on the phone and tell him to change something real quick.

43:47

You have to build it in a way that you can evolve it over time.

43:53

So you're, and another thing to think about is the data format that you would use for an external API is often going to be different from what you would use in Hamma bars, which is just going to be generating HTML.

44:04

And so in API, maybe it's gonna be consumed by some kind of bot, but maybe it's going to be doing analytics on your data.

44:10

And so you might want to have things in a more raw processable form rather than a human readable form.

44:17

So because of all that, our general recommendation is when you're building an API.

44:22

So this is Graph QL, you would be writing custom view models that are specifically designed for that API and satisfy exactly what that API needs to provide and nothing more and nothing less.

44:34

you may be able to get away with using the handlebars views and maybe even view models in your graph I by what does support that.

44:42

But it's gonna be a case by case basis, whether that's actually gonna be a wise thing to do.

44:48

So when you're writing your graphical view models, they're basically going to be the same as the ones we've already looked at with handlebars.

44:53

The main difference is you would not be using those autogenerated view interfaces from the roots.

44:59

Do you would just typically you wouldn't even have a view interface at all, you would just write your view model directly.

45:06

And then you need that view interface annotation we talked about which will then tell bright by that.

45:13

You can then use this with the export the data to the public.

45:17

Basically.

45:20

If you're doing AJ O API, you need the JSON view annotation, which is kind of like those handlebars and the template annotations we looked at earlier.

45:27

If you're doing graphical, you don't need anything special.

45:29

The graphical just works with any view model.

45:35

So let's take a look at some code.

45:51

So our first piece here got an API package here to separate things out.

46:00

And our first piece is this graphical endpoint.

46:07

So bright spot graph QL has sort of two kinds of end points.

46:12

There's a delivery, a content delivery API and its content management API, content management API works with the raw data models.

46:19

It's typically meant for like ingesting data in the bright spot content delivery.

46:24

API works with the view models and it is for g meant for, you know, publishing stuff to the public.

46:32

So in our case, we're gonna be extending content delivery.

46:35

API what we're gonna be building here is a search API.

46:37

So you'll be able to search for recipes by various parameters and then get back the recipe data for them.

46:48

So to make that work, we are gonna extend, implement singleton.

46:53

So what singleton does is bright spot will server start up?

46:58

Bright spot will ensure that a single instance of this class is saved to the database and then no additional instances will ever be created or saved.

47:09

They'll just always use that same one.

47:11

This is gonna be important later for the querying part of the API.

47:19

The main this is just the URL.

47:22

It's gonna live under the main piece here that we need is this entry field.

47:25

So we talked about page entry view and there's the preview entry view.

47:29

Those are for use with handlebars, traditional rendering stack in graph QL, we have something called a query entry field which is analogous.

47:37

So basically this, this, this is just our starting point.

47:39

So in our case, we're gonna write a view model that will be our starting point for them querying into our graph.

47:47

And then here is just the name that it will show up in the schema of our graph QL end point.

47:54

So here's our view model and this is a special case for this search API.

47:59

So the view model is gonna extend, sorry, it's a view model extending the model of the recipe, graphical endpoint itself.

48:08

So you would do this because this pattern combined with this being a singleton will allow us to then add search parameters into our API if you don't do this, then the only thing you can search on is the ID or the URL of the content, which may be enough for a lot of use cases.

48:28

But if you want to have any kind of search parameters, you know, like we want to build filter recipes by difficulty here by the tags, then we would need to follow this pattern.

48:38

So the end point becomes a singleton and then the view model which is our entry field is of that end endpoint itself.

48:49

So you can see here, we've got the view interface annotation.

48:52

And then this name is actually what will show up in the schema.

48:55

Again, if we don't have, we could just do this.

48:59

In which case, this, the schema name would be derived from the V model, probably something like this.

49:05

I forget the exact rule.

49:07

So if you actually want to control the name of the schema, you would supply value here.

49:13

We've got all these different fields.

49:15

The web parameter will inject values into these fields from the graph QL query itself.

49:21

So this is where our search parameters are gonna come from.

49:23

So if we want to be able to search by the title, this will then inject the title from our graph QL query so on for the difficulty, so on.

49:36

and then again with because of this the interface, all the public zero parameter methods will show up in the scheme or our schema as values you can query on or select from your, in your response.

49:52

I guess we'll be able to get the items and we're able to get the next page, we want to pate the results.

50:00

So then this is sort of you can think of this as like a constructor.

50:03

This is one of those save life or not save the view model life cycle methods.

50:08

I talked about we talked about should create earlier on, create will run after should create.

50:13

And lets you just set up parameters effectively is the reason for it.

50:18

It also accepts the view response which is probably gonna be more useful in the handlebars traditional stack.

50:25

This lets you set like cookies and response headers and as well as the response status like 404, 300 or something.

50:36

You could use it to your rear X.

50:39

So in our case, we don't really need that, we just want to use this as a constructor just to fill up some parameters or instantiate some variables for us.

50:48

And then the view response is actually an exception.

50:50

So if you were going to set, like you could use this to do like access restriction.

50:55

If someone's not logged in, then you can send a 403 and you could throw this basically so that the rest of the model and execution will just return an error to the user without you ever rendering out any you know, private data that has to be logged in to see or something like that.

51:13

So in our case, we're gonna just set up some parameters and figure out what page we're on.

51:18

And then we're actually gonna run the query here so that we can then use that query once, run the query once and then be able to get the items as well as the next page off of it.

51:31

Otherwise you have to run the query twice and that would be inefficient.

51:36

So here's our build query method.

51:40

This is an example of querying, not from all but from a specific content type because again, this is an API we're only gonna show visible non archive, non draft content here.

51:53

So this first bit here is we want this query to go to SOLR.

51:58

Because a lot of these parameters will force it to SOLR anyway, these matches parameters.

52:02

So if they ever cross up, if they supply a title or a tag, then we're gonna do a full tech search.

52:08

If they didn't supply that though, we still want to do a full text search just so that the same database is always running these queries.

52:14

And then typically, SOLR has a better performance for lots of joins, basically any time you see an and that's a join.

52:22

And so if we ran this query to my sequel would be pretty slow.

52:26

And solar does a much better job of those.

52:28

So we're gonna force it to solar with this sort of secret handshake here, star match to star, basically just forces it to solar without ever without changing anything else about the query.

52:41

And then we talked about sites earlier.

52:44

So this query, this endpoint will have a site attached to it.

52:50

And so we want to restrict the content to only recipes within that site.

52:54

So if the site was given, then we will restrict the items, the only things visible visible in that site.

53:01

And then the rest of this is just if we, we were supplied a parameter in the graph query, then we will add it to the query.

53:07

So if we have a title, we'll query our plain text title field by full tech search.

53:13

We have a difficulty, we will match only that one difficulty tags and we have generic search terms.

53:20

And this here is allows to do like quoted searches.

53:24

So if we want to search for a specific phrase, this will only match that one phrase and not those words in any random order.

53:31

, except we can do this proximity clause.

53:33

So they can be swapped a little bit and still match.

53:36

But if they're too different, you know, if they've been heavily swapped, then it won't match.

53:41

So you don't, you don't always need this, but I wanted to show you kind of a more advanced full tech search capability.

53:50

So we had all the parameters that make sense, then we returned the query and then up here we did, then we saw the first, you know, just getting the first item earlier, but now we're gonna actually gonna select by an offset in the limit to get a paginated result, page result just has the items in that section as well as the previous, whether there were previous items, whether there was next items.

54:12

So we can use those to generate our imagination parameters, which would be the page number.

54:20

Yeah.

54:20

So with all that, we can now go to the C MS close all this stuff.

54:35

in the developer area here, we have the graphical explorer.

54:38

Actually, the first one we go to show you the API.

54:39

So under here, under admin, we have the API S and then we have our recipe, graphical endpoint.

54:46

This is a singleton.

54:47

So this was generated automatically, we didn't have to come here and, and actually set this up.

54:55

So with that in place, I can then go to the graphical explore and see the scheme of that this generator and actually start running some queries so I can query over the recipes.

55:08

Maybe I want to search for the only expert level recipes and I wanna get the you know, the title the total time.

55:21

So we, we only got one and it has 100 and 45 minutes as the total time to prepare it.

55:32

And there's other parameters here.

55:33

So we have like a title we can search for that should still match.

55:47

So with this, we basically built a search api So our next piece, we're gonna do some really advanced filtering.

56:09

We want to be able to filter on the times, but it's not useful to just filter on a specific time.

56:13

We want to find the recipes that are 60 minutes, we want to find a range.

56:17

And so we can actually support that with a little extra work.

56:22

So we've added a couple of new classes here.

56:25

So all of these annotations we've used so far and the models have been built in, you can actually write your own annotations.

56:33

So we written one called range graphical parameter which let's just query over ranges And the main way to make this work is you have your annotation, of course target fields needs to be retained at runtime.

56:49

So bright spot can find it.

56:50

And then we would need an associated processor that extends this content.

56:58

A room extends this content delivery API web annotation processor.

57:02

So this is basically a specific for use with graph QL.

57:07

So this annotation would not work with handlebars.

57:13

And then it takes our annotation itself as its parameter.

57:17

And so with this, we can then have sort of two things we have to define, we have to define what it's gonna look like what it's gonna look like in the schema.

57:25

And then given a graph QL, how do we then get the data out of it?

57:30

Then use this you know, get an actual range out of the data.

57:35

So here we're defining an object called range.

57:38

It has two fields then and max are both integers.

57:42

So that will show up in our schema.

57:44

And then given that someone used that scheme at a query, we will get our request the data raw data from that query.

57:54

And then this is actually like the annotation of the field.

57:56

If we actually wanted to get data out of those, we don't need that in this case.

58:01

But you could actually have a parameter on your que query on your annotation.

58:06

Like maybe the name of something you could use that to then further process the data.

58:11

So in our case, we expect the input to be a map and we want it to have expected to have two fields a minute, a map.

58:17

So it will directly correspond to the ste A.

58:20

So we're using some bright spot, just helper methods here.

58:23

So collection utilities expects takes an object treats as if it's a map and it lets you pull out keys from that map and then object details to that's the cast things.

58:37

So we'll get this object and we'll cast it to an integer and it does has some conversion logic.

58:43

So this is a string.

58:43

I think it would actually still be able to convert it if the string would match an integer.

58:47

So we got our minute max values and then we're just going to return in a Java class that Java object that just combines those together.

58:54

So this is just like a poo the men of the max.

59:00

So we can just store those together and return them.

59:03

And then in our view model, we're going to annotate all these fields.

59:09

So we have a range parameter.

59:10

That's that class that we just looked at and we have our annotation attached to it.

59:13

So that will then fill it in from the request and we can get our total time, prep time, an active prep time and cook time.

59:20

And all the there'll all be ranges that we can then query over.

59:25

So then down here, we've added some other additional clauses to our query.

59:30

So we have a total time then actually move the logic for querying it inside that class just to save, you know, duplicating the logic.

59:40

So in here, we've got our update query method.

59:42

It takes the query and it takes the field we're going to be searching on.

59:46

And then if we have them in, we're going to say that that field is greater than or equal to the ma max, less than equal to the max.

59:59

So then this will update the query with all those guys if they've been supplied and I think that's all the pieces to it.

1:00:07

So now if we go to is schema, we should have some additional parameters here on the schema on the query side.

1:00:44

Yeah, I would definitely have slowed down.

1:01:09

hm.

1:01:20

You know, try restarting it.

1:01:27

BK is just a shortcut I have for doctor compose.

1:01:53

There we go.

1:01:56

Yeah, like it like this did.

1:01:57

That's the same thing and now I'll restart it, you know, watch the logs again.

1:02:12

And the reason why I filter the logs with grip is by default, all the logs are mixed in.

1:02:19

So you'll see that my sequel logs and the Apache logs and the solar logs all mixed in together and it's a lot of noise really.

1:02:25

You only 99% of the time you only care about the town catalogs, all of your codes running in tomcat.

1:02:36

So all of your log messages will show up in the TOMCAT logs.

1:02:54

OK?

1:02:54

Hopefully, that performance better.

1:02:58

Now, there we go.

1:03:01

So I can also get to that graphical explorer under the developer area here.

1:03:06

And I choose the end point which is the recipe's endpoint and then here it's loaded the schema.

1:03:13

So now we here we have like our prep time so we can filter in things that are easy to make for less than 15 minutes.

1:03:20

And then I forgot to fill in some parameters.

1:03:24

The title and the difficulty, the cook time.

1:03:30

So here's that asparagus one again or we could do a minimum of 15 minutes and then, and we get more recipes.

1:03:42

So that way we can, it's much, you know, I guess you could have done this with a separate field for each one minimum time, prep time, maximum prep time.

1:03:48

But this way, it just makes it a lot cleaner in the schema.

1:03:54

So then our final peace here, We're just adding some additional parameters onto our endpoint.

1:04:20

So we want to be able to restrict access to it, maybe depending on the use case.

1:04:26

And so we would need to have an access option for configuring that we also might need course if it's gonna work with browser app, like a java app or, or javascript app in a browser, we need to have course parameters configured.

1:04:40

And then we also want to be able to attach image sizes to our API so that you can, if you have an image, you can say I want this specific crop size for it.

1:04:56

So you need a theme for that.

1:04:59

So this is just kind of border plate just setting the course parameters.

1:05:04

Actually, I totally forgot we didn't look at the view models.

1:05:08

So yeah, we have our endpoint view model, but for the items, we need to render, we need a V model to render each item item is gonna be a recipe.

1:05:18

So we need another recipe view model.

1:05:20

So here's our, our view model recipe.

1:05:23

That's for the API use case and note here that we have a recipe model for the handlebars use case which we looked at earlier.

1:05:29

So your view interface recipes, what will show up in the schema?

1:05:35

And then here we've got all just kind of the same stuff we had in the handlebars one except the formats are different.

1:05:40

So here I think in the, in the rest in the handlebars, one for all the times we were displaying a, like a human, you know, pretty printed string, describing the time, you know, five minutes or something.

1:05:52

Whereas here in the API version, we're just sending the, the number in minutes.

1:06:00

In that way, we could also, this could have also been like a an iso like a duration stamp or something.

1:06:05

There's any number of formats you could have used.

1:06:11

So, and then for the difficulty, we're gonna return the name, maybe we could also return the code if we wanted like 123 or something.

1:06:21

There's any number of ways you could describe that in your API So then a couple of special things here is rich text and the images so rich text.

1:06:32

Remember before we were using rich text details that I guess we could have used that here still.

1:06:42

But I wanted to show you the underlying library, basically, that's just wrapping this builder.

1:06:47

And so we're actually sort of cheating here.

1:06:49

We're actually going to still use the handlebars view models to render out our rich text element.

1:06:54

So we're basically saying that in this api all the rich text is going to be html.

1:06:59

And so we can use our handlebars view models for all our rich text elements and we need some other format that we have to write custom view models for all of our rich text enhancements, like image or links or anything on any of those things that we want to be able to support.

1:07:16

And I guess we have to use the builder here because we want to get html out if we use the risk text details that returns a few models.

1:07:24

I remember if we go back to recipe.

1:07:29

So these on my return and views, which is not useful for us in, in the graph context because we want to be able to generate actual html.

1:07:41

And so instead of using this builder to build html which returns the string, but it's not a plain text string, it's an html string.

1:07:49

So that's sort of the distinction here.

1:07:50

So we use that for directions and ingredients.

1:07:54

And the title they all text.

1:08:00

the for the image there's this annotation called image attributes which will convert the image into.

1:08:06

Well, so we use this library here to convert an image into a map.

1:08:10

So we have like the source and then with height and other stuff and then this image attributes tells graph QL to then present that in a special format in the scheme of which we can actually go look at.

1:08:20

So if I go here to pick the image, you can see here, we got all these different things we can choose like the URL or give me the width and the height.

1:08:33

And then if we add that to the query, we can see we'll get some additional parameters inside that image area.

1:08:39

So here's the image, we get the different URL S and stuff.

1:08:44

And then for crops, we can pick a specific size by name and that will correspond to because we selected a theme, go back to our API S.

1:09:03

We added that theme annotation, which lets us pick a theme here.

1:09:07

So here we're, we're just using our handlebars theme and that will then expose all of our handlebars theme image sizes into our results.

1:09:21

So I'll be able to pick an image size based on this is gonna be covered tomorrow in the front end training.

1:09:32

If I go here our bundle and choose where is it image sizes config so all of these image sizes have been defined so I can pick any one of these, so I can pick like 120 by 120 as the size that I want.

1:09:49

And then I wanna get the hi and the source and the width.

1:09:59

And so you can see here's an image URL, it's been cropped to 120 by 120 actually load that up.

1:10:05

And so here we can see is a 1120 by 120 version of that image.

1:10:08

So that way consumers api can pick a size that matches their use case if they're making like a mobile app and they can have smaller images.

1:10:15

If it's a desktop app, they can pick bigger images.

1:10:20

And that's all handled by this image attributes, annotation kind of links all that stuff together.

1:10:29

So to make this work, you need to use the image size attributes to actually generate the data.

1:10:33

And then you have the annotation here, I need to have a theme set on your end point which means you need to have yeah, this content delivery of the will annotation or interface on your end point.

1:10:54

And then the final piece to show is the tags.

1:10:59

So here we have the tags and we're using great views just like we did in handlebars, except we're passing in the specific view model we want because again, we don't have any field interfaces here.

1:11:07

We don't have recipe A V I view tags field that does, that wasn't generated for us.

1:11:12

We, we'd have to write that ourselves or we can just directly point to the view model and it'll use that.

1:11:18

And all this is doing is just rendering the rich text to plain text.

1:11:22

So again, if we go over here to pick that, under items, we've got tags, so we get the tag name so we can see some of them don't have a tag, some of them do have a tag.

1:11:38

So they the tag salads.

1:11:43

So I think that's basically everything any questions about any of the graph QL stuff.

1:11:55

OK.

1:11:58

Talked about all this.

1:12:02

Yeah, so I remember just graphical an API we had, we had to kind of add authorization concerns.

1:12:07

Course concerns even talk about backwards compatibility.

1:12:10

But obviously we can't just like delete something out of our view model because then the API is effectively changed.

1:12:17

So, you kind of want to plan things ahead, you can evolve the API every time to add a new field and just kind of deprecate the old one or something like that.

1:12:26

And then you need to think about, you know, do you need kind of a full search api that we just built or is the idea past efficient?

1:12:32

That can be a lot simpler to implement?

1:12:44

OK.

1:12:45

So we got some resources for you.

1:12:49

Everything we've looked at so far as in the training repo everything we've done is on the exercise slash recipe branch.

1:12:55

So you can kind of see every commit that we went through on that branch.

1:12:58

We'll share the slides and recording of the presentation, you can refer back to this and we moved really quickly as a lot of material to cover.

1:13:05

So I encourage you to come back and review it on your own time and kind of study things as as necessary to understand the material.

1:13:13

Here, read me about the doctor container that we're using in that environment if you have any problems with it.

1:13:19

As well as links to our bright spot documentation.

1:13:22

I would encourage you to check out our developer portal and our new platform extensions.

1:13:28

And if you we also have a support portal.

1:13:31

So if you have, you can make the tickets there basically for, for requests for help and, and what not advice.

1:13:39

So contact your product manager for access and I actually have a special guest now to talk to a bit more about the platform extensions because we're pretty excited about those.

1:13:49

So I'll hand it over to Phil to tell you about those.

1:13:55

Well, thank you, Brandon.

1:13:57

I appreciate it.

1:13:58

And you're a, you're a tough act to follow.

1:14:00

I'm glad I'm not here to do any any actual training.

1:14:04

I would not wanna go after you.

1:14:06

So, I'm just here to do kind of a quick commercial on platform extensions, something new that we're doing.

1:14:13

So the idea here is we wanted to be more useful to provide more information to our developer community and make it easier for you to learn and ramp up or even if you're all the way ramped up and when you want to do something really complex, we thought it would be helpful to share some complex code examples.

1:14:34

So, what platform extensions are, are features that were developed for customers.

1:14:42

And we took them and, and made them like generically available.

1:14:46

And so they're out right now on our website for this audience, the the main use is gonna be you know, these are complex features, real life things that were built.

1:14:58

And they're well documented.

1:15:00

They all have java docs as well as user facing documentation, which maybe is because as helpful for visitor.

1:15:10

But they do again is, is real life complex examples.

1:15:13

On the right, we have some examples of, of features that have been done.

1:15:19

And lower down that red button if you're building something or, you know, three months from now, six months from now you're building something that you wanna share with the brothers community.

1:15:30

We'd love to hear from you and Brandon if we can go to the next and my last slide, thanks.

1:15:39

So just to show you per Bright Spot user guide for Bright spot.com, that's where we have all the platform extensions.

1:15:50

And on the left hand side, you can click on the links, there'll be a PDF that get sent out And the links should work.

1:15:58

So you can see all the documentation that's for all of these platform extensions.

1:16:02

And this is kind of a pilot program.

1:16:04

So you can use the red button as well to submit any feedback, but we're hoping this is something that's helpful and, and another way we can kind of encourage and enable you to be awesome bright spot developers.

1:16:19

And that's all I had Brendan, anything to add on these.

1:16:24

Yeah.

1:16:24

So when we share the slides, all these links will be in there, so be able to check this stuff out.

1:16:28

And if you do have things you work on or going to work on that, you think would be useful.

1:16:32

Yeah, you can we'd love to see what you've done and, and share it with the rest of the community.

1:16:37

Thanks, Phil.

1:16:40

Yeah.

1:16:41

One other announcement here is we have upcoming bright spot user conference at the end of April in Reston at the Hyatt Regency, Reston encourage you to come join us for a couple of days of insights and connection with the bright spot community.

1:16:57

We'll give you a glimpse of the product road map.

1:17:00

You can hear from analysts and customers on digital experiences and dive deeper into the developer focused topics like A I bright spots API S.

1:17:10

We'll have some round table topics and even more.

1:17:14

And you can view the agenda and register on our website.

1:17:18

Again, this link will be in the, in the P DS slides that we share with you so you can register and at 10, we'd love to see you.

1:17:26

Again, that's the end of next month.

1:17:30

So does anyone have any questions about the training or the platform extensions or the user conference?

1:17:38

I'll give you a minute or two here to enter them in the Q and A.

1:17:44

So this you know, feel free to type anything in happy to answer any questions.

1:17:49

And then obviously, if you think of questions later on, I encourage you to use the support portal, get access to that.

1:17:57

And you could submit the ticket there and someone from bright spot will get back to you.

1:18:46

oh And one reminder is if you're interested in the front end training, that will be tomorrow.

1:18:53

So we'll cover the front and half of the, the right spot development experience and that will be focused on the traditional stack with handlebars and doing the handlebars bundle.

1:19:03

We won't, we won't be covering anything about graph 12 or headless in that presentation.

1:19:11

So I'll wait another minute for any questions.

1:19:39

OK.

1:19:40

I guess there are no questions.

1:19:41

So, thank you all for joining and best of the luck, best of luck developing a bright spot.

Get in touch

Ready to upgrade? Get answers to all your questions, guidance on when you should upgrade, and more by contacting your customer success representative.

Email our Support Team Now