[{"data":1,"prerenderedAt":461},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"request-review-19659":121,"request-review-19659-next":189,"sales-reps":209},{"items":4},[5,29,49,69],{"id":6,"title":7,"url":8,"page":8,"children":9},"522e608a-77b0-4333-820d-d4f44be2ade1","Solutions",null,[10,15,20,25],{"id":11,"title":12,"url":8,"page":13},"fcafe85a-a798-4710-9e7a-776fe413aae5","Headless CMS",{"permalink":14},"/solutions/headless-cms",{"id":16,"title":17,"url":8,"page":18},"79972923-93cf-4777-9e32-5c9b0315fc10","Backend-as-a-Service",{"permalink":19},"/solutions/backend-as-a-service",{"id":21,"title":22,"url":8,"page":23},"0fa8d0c1-7b64-4f6f-939d-d7fdb99fc407","Product Information",{"permalink":24},"/solutions/product-information-management",{"id":26,"title":27,"url":28,"page":8},"63946d54-6052-4780-8ff4-91f5a9931dcc","100+ Things to Build","https://directus.io/blog/100-tools-apps-and-platforms-you-can-build-with-directus",{"id":30,"title":31,"url":8,"page":8,"children":32},"8ab4f9b1-f3e2-44d6-919b-011d91fe072f","Resources",[33,37,41,45],{"id":34,"title":35,"url":36,"page":8},"f951fb84-8777-4b84-9e91-996fe9d25483","Documentation","https://docs.directus.io",{"id":38,"title":39,"url":40,"page":8},"366febc7-a538-4c08-a326-e6204957f1e3","Guides","https://docs.directus.io/guides/",{"id":42,"title":43,"url":44,"page":8},"aeb9128e-1c5f-417f-863c-2449416433cd","Community","https://directus.chat",{"id":46,"title":47,"url":48,"page":8},"da1c2ed8-0a77-49b0-a903-49c56cb07de5","Release Notes","https://github.com/directus/directus/releases",{"id":50,"title":51,"url":8,"page":8,"children":52},"d61fae8c-7502-494a-822f-19ecff3d0256","Support",[53,57,61,65],{"id":54,"title":55,"url":56,"page":8},"8c43c781-7ebd-475f-a931-747e293c0a88","Issue Tracker","https://github.com/directus/directus/issues",{"id":58,"title":59,"url":60,"page":8},"d77bb78e-cf7b-4e01-932a-514414ba49d3","Feature Requests","https://github.com/directus/directus/discussions?discussions_q=is:open+sort:top",{"id":62,"title":63,"url":64,"page":8},"4346be2b-2c53-476e-b53b-becacec626a6","Community Chat","https://discord.com/channels/725371605378924594/741317677397704757",{"id":66,"title":67,"url":68,"page":8},"26c115d2-49f7-4edc-935e-d37d427fb89d","Cloud Dashboard","https://directus.cloud",{"id":70,"title":71,"url":8,"page":8,"children":72},"49141403-4f20-44ac-8453-25ace1265812","Organization",[73,78,84,88],{"id":74,"title":75,"url":76,"page":77},"1f36ea92-8a5e-47c8-914c-9822a8b9538a","About","/about",{"permalink":76},{"id":79,"title":80,"url":81,"page":82},"b84bf525-5471-4b14-a93c-225f6c386005","Careers","#",{"permalink":83},"/careers",{"id":85,"title":86,"url":87,"page":8},"86aabc3a-433d-434b-9efa-ad1d34be0a34","Brand Assets","https://drive.google.com/drive/folders/1lBOTba4RaA5ikqOn8Ewo4RYzD0XcymG9?usp=sharing",{"id":89,"title":90,"url":8,"page":91},"8d2fa1e3-198e-4405-81e1-2ceb858bc237","Contact",{"permalink":92},"/contact",{"items":94},[95,101,107,113],{"id":96,"title":97,"url":8,"page":98,"children":100},"8a1b7bfa-429d-4ffc-a650-2a5fdcf356da","Cloud Policies",{"permalink":99},"/cloud-policies",[],{"id":102,"title":103,"url":81,"page":104,"children":106},"bea848ef-828f-4306-8017-6b00ec5d4a0c","License",{"permalink":105},"/bsl",[],{"id":108,"title":109,"url":81,"page":110,"children":112},"4e914f47-4bee-42b7-b445-3119ee4196ef","Terms",{"permalink":111},"/terms",[],{"id":114,"title":115,"url":81,"page":116,"children":118},"ea69eda6-d317-4981-8421-fcabb1826bfd","Privacy",{"permalink":117},"/privacy",[],{"description":120},"\u003Cp>A composable backend to build your Headless CMS, BaaS, and more.&nbsp;\u003C/p>",{"id":122,"slug":123,"vimeo_id":124,"description":125,"tile":126,"length":127,"resources":128,"people":8,"episode_number":132,"published":133,"title":134,"video_transcript_html":135,"video_transcript_text":136,"content":8,"status":137,"episode_people":138,"recommendations":167,"season":168,"seo":188},"d66c1e46-cc57-49fe-a914-2e440bbc1576","19659","986515873","Rijk, Jonathan and special guest Hannes discuss WYSIWYG Linking Existing Files.","4aa280eb-c685-4d24-b8d0-96477cd047d9",52,[129],{"name":130,"url":131},"Discussion","https://github.com/directus/directus/discussions/19659",9,"2024-07-25","WYSIWYG Linking Existing Files","\u003Cp>Speaker 0: Welcome everybody once more. We're ready to rock. Welcome to request review. Daniel is out this week, so we got a very special guest for you here. Say hello.\u003C/p>\u003Cp>Hello. Hello. Good day.\u003C/p>\u003Cp>Speaker 1: Thomas. Welcome to the welcome to the phone.\u003C/p>\u003Cp>Speaker 2: Welcome to\u003C/p>\u003Cp>Speaker 0: the having me. So today, we're gonna be chatting about yet another feature request. And this time, Jonathan, what have we pulled up?\u003C/p>\u003Cp>Speaker 1: We have the lovely improving the way that files are injected in the WYSIWYG editors, is the discussion for today. I believe the extensions team may actually be taking this on behind the scenes in a in a short term solution. But for now, it'd be nice to understand the requirements, the the gotchas, the whys and wherefores, the way that current the way we currently add links and images and videos within a WYSIWYG field. So you can edit links, move your links, take a look at your links here. But according to the to the request, they'd like this to be a little more fully featured in the way that the tags and things are added around the HTML under the hood.\u003C/p>\u003Cp>It's my understanding. But I'm no front end expert, so let's see what we can figure out and decipher.\u003C/p>\u003Cp>Speaker 0: Alright. So this I can feel it. This is not one of those where it feels like, oh, that that should just be one button. Right? That's easy enough to do.\u003C/p>\u003Cp>Alright. So starting at the basics and pulling it up, if you seem to look enough to decide over here, by the way, I just added the same thing up, but a little bit bigger. The goal here is basically just like you can put in an image, you wanna link to a file. Right? Easy enough, you\u003C/p>\u003Cp>Speaker 1: think.\u003C/p>\u003Cp>Speaker 0: Inserting a link to a file in its purest form shouldn't be too crazy because we know what the IDs of the file. We know where you would find it slash assets. It's the same as an image. Right? Mhmm.\u003C/p>\u003Cp>Just as with an image though, the question immediately becomes permissions? Big one. Right? The user that is inserting the file has permissions to see those files and then select it to be linked. But that doesn't mean that the person on the other end that is that's seeing this HTML rendered on a website or something also has the permissions to that same file.\u003C/p>\u003Cp>Right? And we've seen this before with images.\u003C/p>\u003Cp>Speaker 2: Yeah. I was trying to say it's the same problem with images. Right? Because images share the same\u003C/p>\u003Cp>Speaker 0: properties. Yeah. So the way we do it for images right now is we do have that option where you can sort of hard code in one of the the tokens if that's the way you wanna do it. You could, of course, make permissions a little leaner for reading images. Although that way, you know, it's a little bit more than permission set up.\u003C/p>\u003Cp>Not ideal. You can have people log in, of course, on your own front end and then use that for permissions. But it feels like even with the link to files, it might get a bit worse because at least with an image, you can immediately see if it doesn't work because you get a broken image. Right? Because you get a permissions issue.\u003C/p>\u003Cp>So as the person that's implementing it, it becomes a little bit more obvious what's going on. But if you just have a link and the link doesn't work for some, but it works for others, that's tricky. That's tricky. So I think as per usual for for those who haven't seen one of these before, the way this oftentimes works is that, you know, we take the smallest feature request that that is in here. The what is the the status?\u003C/p>\u003Cp>What is the minimum viable? Then convergently think about, okay, what scope does this live in? What are all the edge cases that we have to think about? What are the problems that will come up? And then sort of take it back down to, okay, what can we actually do today?\u003C/p>\u003Cp>Right? And what is the long term plan and how do we get there? So I think to me, an important one for to make this one work as expected is all around permissions. So I'm actually very glad that you're here, Thomas, because you just work a lot on permissions as part of this, v 11 release in the RC that is out now, in the GA coming up very soon. Any immediate thoughts on the permission set up for this?\u003C/p>\u003Cp>Speaker 2: Especially with files, I do worry that giving read access to a lot of files like you suggested previously is a big foot gun for people because you might store private files. You might store exports from collections or something in your files and giving, like, real access to any of them feels wrong to do it as a blanket or something. That's like a blanket permission. We we, the best possible way would be to surface that information somehow. Like, how visible is a link or how visible is, like, something you link to, since even if you're testing out the link on your live preview or something, you still might be logged into your CMS behind the scenes.\u003C/p>\u003Cp>So for you, the link also works. So you have would have to test it in a private window or something. But then again, we could show you, hey. This is how the public what the public permissions for this file look like. Users might not be able to access it.\u003C/p>\u003Cp>On the other hand, we don't know about your setup. Right? So we don't know, are you actually just visiting those with the public public permissions? Or are you adding a token? Or are you doing some other stuff with it since we don't know really what is the implementation that relies on it.\u003C/p>\u003Cp>So it's hard to think about a catch all solution that would help people not shoot their foot. So right now with images, we do it. We basically let you do anything with it, and don't hold your hand while doing it. And I wonder if that is also the solution in this case. So just provide more functionality.\u003C/p>\u003Cp>But if stuff breaks on your end, you have to be aware of it. Seems could be a documentation issue, but then again, the people reading our documentation aren't actually the users using the product in the end maybe.\u003C/p>\u003Cp>Speaker 0: Yeah. It's a tricky one. And and to your point\u003C/p>\u003Cp>Speaker 2: of, like, where do you wanna put this education?\u003C/p>\u003Cp>Speaker 0: Right. Yeah. And and to your point, we when we had this issue for images, the conclusion was, well, you know what? Let's just make all the options available and and put the burden of setup on the the end user to make sure that they're using it right instead of on us to to try to help you with it. It did this made me, think of another feature request that we I don't think we've discussed quite yet here, but it gets come up a couple of times in the past, which is the nested relational permissions, so to speak, where you could say, files are inaccessible by default, but you can allow file reads if they are being read through another relational field.\u003C/p>\u003Cp>So for example, you could say files are off limits, but if you look up an avatar through a user, you can still access that one avatar in some of its fields, but only if you go in relationally through a user.\u003C/p>\u003Cp>Speaker 2: How does it work with the assets endpoint though?\u003C/p>\u003Cp>Speaker 0: Right. That's where it gets interesting because I'm I'm just wondering coming from. Yeah. No. Exactly.\u003C/p>\u003Cp>I'm just curious if there is an angle to that, because it made me think of this where I'm like, oh, if you assign an image or an asset into a WYSIWYG blob, maybe we can have it rely on a similar idea of permissions where it's like, okay. The things you make available within, one of those fields can be read in a certain way, but not others. Again, just convergently thinking about it.\u003C/p>\u003Cp>Speaker 1: Do you have any other issues? Sorry. Good. I I don't wanna distract you from a thought. So go ahead, Hans.\u003C/p>\u003Cp>Speaker 2: The the issue I'm seeing with that is, again, like, accessing some data of that asset, totally fine. I can see that work, how it would be set up in permissions.\u003C/p>\u003Cp>Speaker 1: I\u003C/p>\u003Cp>Speaker 2: would be able to do, like, the relational reversal of permissions or something or for items. But since the asset endpoint doesn't know about it, we are still at some point where we why would this user be able to see this? Because then we would have to look up. Are there any permissions that could allow it? And so it gets it gets really complicated on the asset end point.\u003C/p>\u003Cp>Like, the actual, like, file permissions isn't too much of a problem, but all of the metadata, but the whole, assets itself isn't too trivial to figure out Yeah. Image for that one.\u003C/p>\u003Cp>Speaker 0: Maybe. Again, divergently thinking about this. Maybe the, there's an in between where we can do kinda like what what s 3 supports and and some of those storage providers where you have, like, a signed link that is unique to where it was used. So we can know from the way the asset was accessed, what field in what collection it was accessed from. And then that way, we can do the relational permissions thingy instead of relying on having to search.\u003C/p>\u003Cp>Because to your point, if you open an asset and we have to search through every single field to see if there's any chance that you used it, that feels crazy. That feels insane. But if we make it a unique link that is unique to that collection of field combination, then that simplifies it a little bit. Because now we do know A\u003C/p>\u003Cp>Speaker 2: lot of things.\u003C/p>\u003Cp>Speaker 0: Where it was accessed from. Yeah.\u003C/p>\u003Cp>Speaker 2: Which effectively could be something like an access token for that.\u003C/p>\u003Cp>Speaker 0: Yeah. Effectively. So so it could be it's it's effectively just just a token with that connects the dots between you're trying to access this image or this asset through an HTML blob, that is on your website. And because we generated a custom link for it, we know what that blob is, and then we can also help you debug, you know, if your file got, is being accessed from sources that you didn't expect it to, we can show you. It's like, well, you got it read from your your HTML body and this and this article or something.\u003C/p>\u003Cp>Right?\u003C/p>\u003Cp>Speaker 1: Interesting. I like it. That was my thinking as well was either inherit permissions for the item, but then as Hannes noted that the asset endpoint has no idea about that currently. Now that doesn't mean that we don't enhance or find a way, kind of a unique URL or a unique identifier that indicates where this is being used. We've actually seen this.\u003C/p>\u003Cp>There's another request out there about knowing what what files are associated with which items. Right?\u003C/p>\u003Cp>Speaker 0: It's Right. Right.\u003C/p>\u003Cp>Speaker 1: We we have this problem right now without writing some very complex hook logic that track tracks through a whole set of relationals. You know, we don't because that asset endpoint is kind of independent of all of that and files kind of live independent of all of that, The way that the relational is unless you're forcing relational reverse creation, which then makes your file through, you know, direct us files gets really ugly really fast that way. We have this it's a fairly common request that we see from clients of, I wanna know what my files are associated with. Right? I wanna be able to see that and understand that.\u003C/p>\u003Cp>So This\u003C/p>\u003Cp>Speaker 0: is this is also an interesting topic in the whole security versus usability piece. Because on on from from a user experience and even a developer experience, if I have a, public HTML blob and I put an image in there, I kinda assume that that image is now also in that same public blob. Right? So I would assume it to work. At the same time, if you if if we were to make it that way where it's like, oh, whenever you put an image inside of a HTML thing, it's now public.\u003C/p>\u003Cp>You can accidentally expose a whole lot of stuff. Right? Because it's it's a little opaque under the hood. From a security perspective, it's a little tricky.\u003C/p>\u003Cp>Speaker 2: Is it though? I guess again, it's really one of those, like, what are all the different use cases Directus is used in kind of problem. And, like, who are the actual end users and who has who has been educated about that problem, caveat, whatever we wanna call it. Because I I generally like the idea like, it doesn't really have anything to do with that, feature request in general since that seems a lot more low level. But I do like the general idea of having something like a unique or signed, asset URL, which can be used to conditionally allow access to something.\u003C/p>\u003Cp>Speaker 1: And so generating something could\u003C/p>\u003Cp>Speaker 2: be go for it. Sorry.\u003C/p>\u003Cp>Speaker 1: No. No. I was just curious. So when you're generating, you know, like, this this input screen right here, so you'd have a unique URL that's being generated here, right, in some way, shape, or form, whether that's a tag on the end of the, you know, asset endpoint that gives you a question mark, you know, unique key. That has to be stored then.\u003C/p>\u003Cp>Is that stored then as part of the director's files or a relational to the director's files kind of a system table where we store those unique IDs? And not so much implementation. Just I I'm thinking about so you'd have that ID. But you'd also be able to see that here. Could you also or optionally ensure that you give, you know, an access token option here?\u003C/p>\u003Cp>So as an actual field input where you could supply an access token or even a default access token for as part of the admin configuration. Because once again, I don't necessarily want users putting in access tokens, but have a way for the administrators to get to apply an access token that has the appropriate permissions for the file.\u003C/p>\u003Cp>Speaker 0: That is the key piece there. That last that last little snippet of the sentence is the most important crucially important part that has the appropriate permissions for the files. The foot gun with the excess token is that somebody just puts in their own users' access token, YOLO it, and then it works. But now you're technically exposing that all over the place, right?\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Because once you're, you're putting that excess token in the image and HTML, it's just a query parameter. So therefore, wherever you render it, somebody can see it. You have to consider it public.\u003C/p>\u003Cp>Speaker 2: See it. They can\u003C/p>\u003Cp>Speaker 0: So what you're effectively doing is you're not using the public role for those images. You're just using a token that you then make public. Right? So that last part is why the sort of developer or user experience or both. I mean, just the experience as a whole for using the static tokens as way to get access to those asset in a link or an image or otherwise.\u003C/p>\u003Cp>There's a couple of food guns, right, that you gotta be aware of that people may or may not be. Because again, that's one of those things where, you know, if you explain it, that makes a lot of sense. But if you don't know and you just wrongly assume you could put in any access token, you can be in a in a world of trouble. Right?\u003C/p>\u003Cp>Speaker 1: Absolutely. K.\u003C/p>\u003Cp>Speaker 2: Which makes me wonder if that is something I haven't really thought about all the ash cases, but it's something probably we can solve by using a j signed JWT or something that just scopes it to that specific item in a collection, then we don't even have to store that in the database, assuming that you have access to that. Because then in the asset endpoint, we could check, hey, does the user like, this request actually have access to that item, which is signed in the JWT? And that way, we can verify the access. And if that basically overrides whatever, like, more restrictive permissions you have, you would be good to go only for that specific item where that image is linked in.\u003C/p>\u003Cp>Speaker 0: Yeah. So we're basically saying\u003C/p>\u003Cp>Speaker 2: Linked or whatever.\u003C/p>\u003Cp>Speaker 0: Try to summarize that so I understand that we're basically saying if you attach an image or an a file, a general asset, any asset to an item in a WYSIWYG or relational interfaces, maybe even anywhere else. Well, relational interface, I guess not because then you just have a data point, not a file. But if you have an asset link inside of a Wizzywig or a block editor, some of those markdown, we use a signed link that contains what collection and item it came from. And then we can use the permissions for that item instead of the permission for files. So therefore, if you have read access to that HTML field, you defect don't have read access to the images within it.\u003C/p>\u003Cp>That feels like a very clean setup as far as I'm concerned.\u003C/p>\u003Cp>Speaker 2: Again, probably also food counts with exposing files or people linking to files, which should be internal at that point. It's more of a shooting your own foot than more, of a case where we are trying to prevent it the best we can. Yeah. But that's,\u003C/p>\u003Cp>Speaker 1: that's kind of already here anyway. Right? Because you can restrict the files and folders that this has access to. Right? In the in the actual WYSIWYG config, you can, I I think Do\u003C/p>\u003Cp>Speaker 2: the root folder or something? It's what we do. Right?\u003C/p>\u003Cp>Speaker 1: It should only be what this user has access to. Right? So if I add\u003C/p>\u003Cp>Speaker 2: Add in general. Yeah. Yeah. For sure.\u003C/p>\u003Cp>Speaker 1: If I add an image, you know, I can add or edit. I don't wanna do that.\u003C/p>\u003Cp>Speaker 2: I wanna So your your condominiums and images.\u003C/p>\u003Cp>Speaker 1: Yep. This is my my permissions restrict me to whatever I have access to, and so I shouldn't be able to technically insert something that I shouldn't be able to now. Could get a little more complex, but I like the that was my thinking and couple of notes that I took. I want a way to restrict the permissions to based on the item's permissions. Yep.\u003C/p>\u003Cp>So I like the\u003C/p>\u003Cp>Speaker 2: change that we do to go with Amazon. Yep. It's interesting. And it's it also could be something that is just a tick mark or something in that dialogue, which can say, hey. Create a signed link or create an unsigned link because people might not want to have that additional check, but just generally make stuff public or something.\u003C/p>\u003Cp>But it could be an option either for the end user to decide or for the admin setting it up to decide. I feel like that is a good way of doing it also to introduce it in a non breaking way if you ever wanna introduce it in just a feature release.\u003C/p>\u003Cp>Speaker 1: Judd's asking if we could display make it so that it's visible in the sense of you'd be able to see who has access to that data. This is not great. Can we do that? If if we're\u003C/p>\u003Cp>Speaker 2: doing it with a token, probably not.\u003C/p>\u003Cp>Speaker 1: No. But if we use, like, the scope JWT, would you be able to see so if I was in here looking at this, would I be able to see could you display here who has access? That seems crazy.\u003C/p>\u003Cp>Speaker 2: Whoever has access to, like, whatever item you're inserting this into has access to that file in or something. So it's not something where we have either this user, like, harnesses you in your file or something, or or do they do, like, color eagle? Or I don't know. So I didn't at least with the route we're planning on going or considering going, that wouldn't be the case.\u003C/p>\u003Cp>Speaker 0: Although, now I'm thinking about it. If you have the we use the permissions for the item for the asset endpoint, you still have a similar case where you could render the HTML somewhere for a person that's not logged in. Although, I guess, at that point, it's it's more expected. But you still Oh, you\u003C/p>\u003Cp>Speaker 2: mean, like, service side rendering or something where you have Yeah. Token or, like, some service level token that actually does the rendering and you still break your links?\u003C/p>\u003Cp>Speaker 0: Yep.\u003C/p>\u003Cp>Speaker 2: I mean, at that point at that point, you wanna do like, you wanna manually track what is publicly available or something, I feel like.\u003C/p>\u003Cp>Speaker 0: Yeah. I'm I'm just wondering because still divergently thinking what we can do to make this just very obvious in how it works, but still secure at the same time. Like, I'm also thinking an alternative, and this is just completely other idea. But if there is a native dedicated public folder in the file library, just a different root folder in the file library that is always public, you make it super obvious that, oh, if you wanna put a public image in your article, pull it from the public folder, right, or move it there first to make it public and then use it in your article.\u003C/p>\u003Cp>Speaker 2: Now I'm wondering if that is way too CMS specific.\u003C/p>\u003Cp>Speaker 0: It is. Although, I guess, yeah, public sharing of files, yes, no, yes, no, maybe. I don't know. Tricky. I know a lot of dams\u003C/p>\u003Cp>Speaker 2: are all public by default, which\u003C/p>\u003Cp>Speaker 0: is also a strange idea.\u003C/p>\u003Cp>Speaker 2: Lot of what? Sorry.\u003C/p>\u003Cp>Speaker 0: I know a a couple of dam providers that default to everything being public for ease of use and then you have the opposite where you can mark certain files as private instead of the other way around. But that is also\u003C/p>\u003Cp>Speaker 2: I mean, we do allow you to conf to set that up. Right? We do allow you to set up permissions. Hey. You're allowed to access files which are located within this folder\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 2: Through public permissions, public policies, whatever you wanna do it with. So we already have that. It's just not there by default.\u003C/p>\u003Cp>Speaker 1: We tried again, I'm kind of okay with that from, I I I don't think we have nobody complains about the ability to make something public. It's more so the one. The other thing we should make sure we come back to in this, is it's also about the way it's being embedded. I think the HTML coding that's actually being used, making it easier to con\u003C/p>\u003Cp>Speaker 2: Yeah. That is something we've glanced over so far. Right? Because\u003C/p>\u003Cp>Speaker 1: Yeah. We're more concerned. Security is to me the the first thing we should be thinking about.\u003C/p>\u003Cp>Speaker 2: Yeah. Because for an image, it's kind of there's an obvious way of how to do it. But for an a tag, I mean, there are some standard attributes we wanna put in there. Yeah. But now the question is, like, what is a standard way of linking to something?\u003C/p>\u003Cp>It's probably an a tag, but is it something that we want to make configurable if use people are using it in service side reactor ending, if you're rendering whatever? Or is that so much out of scope that we say, if you're doing this, you better use the block editor and do your custom rendering pipeline or something or use markdown and go with that? Is it something where we wanna be really open to not rendering just an a tag, but something user defined? Because in the initial request, it looks like it's the requesting only a tags or link tags, whatever. I haven't really been able to see that in the request there.\u003C/p>\u003Cp>Speaker 1: Yeah. Here. Well, that's an alternative. This is what they're doing as an alternative. They're doing some things strangely behind\u003C/p>\u003Cp>Speaker 2: the scenes. Rendered as oh, I think it's a typo. The link should be rendered as an tag. So as a tag maybe in the first sentence or the first paragraph, the motivation.\u003C/p>\u003Cp>Speaker 0: I think that's supposed to be as an as an anchor tag. Yeah.\u003C/p>\u003Cp>Speaker 2: Yeah.\u003C/p>\u003Cp>Speaker 1: So what is this considered currently when we're inserting? Because this is an h ref. Right?\u003C/p>\u003Cp>Speaker 2: It's an image.\u003C/p>\u003Cp>Speaker 0: Is it it's an anchor text. I mean, for for images, it's an image. And for for links, it's just an anchor, an a tag.\u003C/p>\u003Cp>Speaker 2: But a link is not something where we directly allow linking to any records or something. Right? It's just a insert user.\u003C/p>\u003Cp>Speaker 0: And it's just a link. Insert a link.\u003C/p>\u003Cp>Speaker 2: Yep. Yeah. Yeah. Yeah.\u003C/p>\u003Cp>Speaker 0: So so this feature request in its base in most basic form, it's just when I click add a link, allow me to choose a file instead of add a link. That's that's that's bare bones what this is saying.\u003C/p>\u003Cp>Speaker 1: So the same way that we can do image the image and media because they're applying specific tags. But if you're just adding a tag, right, so if I just say I wanna, you you know, make this a link or if I'm just creating a link, I have to specify the URL. So what they\u003C/p>\u003Cp>Speaker 0: want is the to a static file, you'd now have to type in mydirectors.com/asset/whatever the ID is. And and this is this in its simplest form, this is basically just saying, give me a link to a file.\u003C/p>\u003Cp>Speaker 1: Give me a link to a file, same as when you're doing this, where you're inserting an image and you're searching. So they want the they want this regardless of image or anything else. Got\u003C/p>\u003Cp>Speaker 0: it. It's basically the exact same flows in certain image except it pops in a link instead of an inline embed.\u003C/p>\u003Cp>Speaker 1: So you'd have another you'd have essentially another option here that's, you know, insert a direct as file or insert a file. Right? And maybe the image library filters and searches. Got it.\u003C/p>\u003Cp>Speaker 2: Yeah. Which means we immediately derailed this discussion by going, hey. What about permissions?\u003C/p>\u003Cp>Speaker 0: Exactly. That's that's the beauty of these these sessions, Thomas. Welcome aboard.\u003C/p>\u003Cp>Speaker 1: Welcome to our crazy party. I still like the idea of making this this better. Right? So making this more understandable and more usable.\u003C/p>\u003Cp>Speaker 0: Yeah. Image images within WYSIWYG are are infinitely. Because I have another topic if you wanna just derail it a little bit more on this at the other the other way, which is migrations to and from different URLs. If you change the URL of your direct installation, this technically breaks Yeah. It's paint.\u003C/p>\u003Cp>Speaker 1: Actually, I it happened on this very record. I popped this open in preparation for this this morning, and I was like, why is my image not showing up? And I right clicked and it was I'd actually migrated this from, another project, a while back.\u003C/p>\u003Cp>Speaker 2: You see.\u003C/p>\u003Cp>Speaker 1: And now my URL was just back\u003C/p>\u003Cp>Speaker 2: in just this morning.\u003C/p>\u003Cp>Speaker 1: Yep. So when I was looking at the, you know, the link here, I had to actually adjust this to my correct URL. So this is a this is a known issue when we're doing, like, data migrations and things. We've got, our support team has some scripts and things that we'll go through and find try to find and replace known values here. But it's a\u003C/p>\u003Cp>Speaker 0: It's a tricky problem to solve because it's it's by definition, the WYSIWYG one is the trickiest because it is HTML. It's not a pro like a readable format necessarily. I mean, you know.\u003C/p>\u003Cp>Speaker 2: It's something that is like easily solved in I don't know. Like, for example, our block editor or something, right?\u003C/p>\u003Cp>Speaker 0: Right. Yeah.\u003C/p>\u003Cp>Speaker 2: Image could\u003C/p>\u003Cp>Speaker 0: just be nested in JSON or something\u003C/p>\u003Cp>Speaker 2: you can even learn back. Yeah. Yeah.\u003C/p>\u003Cp>Speaker 0: Because you're also dealing with an ID and not a full link. That helps.\u003C/p>\u003Cp>Speaker 2: It's best case best case, it shouldn't even include the link. Like, I don't know what our block editor does, to be honest, but best case, it should just be a acid link or something that just includes the ID of that item you're interested in or that acid.\u003C/p>\u003Cp>Speaker 0: Right. The tricky bit is if you plop that into a browser, nothing shows up. Yeah. It doesn't\u003C/p>\u003Cp>Speaker 2: know what to do with it. Yeah. Yeah. For sure.\u003C/p>\u003Cp>Speaker 0: So you, you have very tricky, tricky setup where that is that is always an issue. And and I'm more than happy because we we have a little bit of time like Lockheed here to to try to to noodle on some ideas for ways around that. Because, you know, some some more CMS specific platforms, they use, like, short codes or something within the HTML. Right? Where it's just like bracket asset ID this and then go figure it out in in post processing.\u003C/p>\u003Cp>That is an option, but that requires the user of\u003C/p>\u003Cp>Speaker 2: the Custom.\u003C/p>\u003Cp>Speaker 0: The output to do something, or we have to have some sort of custom post processing set up for some fields where we say, oh, if you have an HTML field, we'll search through short codes and and execute them. Interesting. Or Yeah. Because custom web components.\u003C/p>\u003Cp>Speaker 2: Custom web components. We could provide a set of custom web components that is a director's image and that knows your CMS location and then just needs the ID, which is effectively what we're doing for Nuxt. Right? There is, like, a country like a Nuxt image module direct as Nuxt module, which is, like, the base URL, and then it just knows the image ID and figures the rest out.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 2: So that could be an option for the WYSIWYG that it's like, hey, if you're using a custom image resolver or something, you could just insert the ID in the link.\u003C/p>\u003Cp>Speaker 0: You can't use the face tag outside of the head, could you, in HTML?\u003C/p>\u003Cp>Speaker 2: Oh, don't ask me.\u003C/p>\u003Cp>Speaker 0: I don't think so. I think base is only in the head. The for that that's just I'm not just thinking HTML brain because you were saying it should just be, like, a relative link instead of a full URL. More or less, you were talking just the ID. But I'm like, if you make it slash assets slash the ID, then it will work within Directus presumably, but it will not in your own website.\u003C/p>\u003Cp>But a base tag in HTML says, you know, graphics every single link to everything with this domain, which could be work around for that. Right? Where you just have a base tag that says, okay, make the base tag, the direct installation, and then it would just work. But I think a base\u003C/p>\u003Cp>Speaker 2: tag breaks every relative link on your Right. Right.\u003C/p>\u003Cp>Speaker 0: Right. I was gonna say breaks every relative link in your whole package. That's what I'm saying. If only there was a locally scoped base tag where you can just say only within the main tag of my Dom is the base tag something else. But, yeah, I don't think that's gonna be a good solution for this.\u003C/p>\u003Cp>Custom web component is an interesting one too because it requires, you know, a bit of custom JavaScript that needs to happen on every single website that wants to use this, and it's just just a bit of yeah. It is\u003C/p>\u003Cp>Speaker 2: one possibility. Yep.\u003C/p>\u003Cp>Speaker 0: Yeah. I think, if if we if we rely on more of a short code, based approach with some post processing, then you make the migration between projects really lightweight, assuming there's a way to skip the post processing. And then the API output could still be the full URL, but we rely on a short code in the database. So therefore, the value that we store is just like bracket asset ID with all the parameters that you want, and then we generate those into images on the fly. But it's just a bit of a I don't know.\u003C/p>\u003Cp>It's\u003C/p>\u003Cp>Speaker 2: It it doesn't feel like our responsibility necessarily. Yeah. That's kind of why I'm struggling. Same with, the public folder or something. That that feels too much like forcing one way of solving it on people.\u003C/p>\u003Cp>Speaker 0: Yeah. You're not\u003C/p>\u003Cp>Speaker 1: wrong. You're\u003C/p>\u003Cp>Speaker 0: not wrong. Tricky bum. Tricky bum. Oh, one more thing related and related, maybe a bit of both. For images, we just plop in an image tag.\u003C/p>\u003Cp>Right? That's that's what what it does. Do we want to support any other file types that are in line? Because videos images are in line, but if you link to a PDF file or something, Do we want\u003C/p>\u003Cp>Speaker 1: to It comes up. We we see that request as well as additional, like, inline viewers. Right? PDFs.\u003C/p>\u003Cp>Speaker 0: Right.\u003C/p>\u003Cp>Speaker 1: Docs, you know, spreadsheets, whatever. Being able to actually have a preview of what that looks like.\u003C/p>\u003Cp>Speaker 0: The tricky thing with those things is that they're all custom front end frameworks or plugins or libraries or web components. It's not it's not a browser standard. So if we wanna go that route, we would have to have some sort of way where you can configure what markup it spits out if you insert a PDF. Because then you get to choose between, oh, when when I'm inserting a PDF, I'm gonna insert my little HTML markup that spits out a PDF preview or something instead of just the link.\u003C/p>\u003Cp>Speaker 1: Yep. So there's that's that's another common thing that we see on those types of fields. Right? So if I go to paragraph here\u003C/p>\u003Cp>Speaker 2: Which already screams use a freaking block editor or anything other than a wizarding question.\u003C/p>\u003Cp>Speaker 0: I you're not wrong. You're not wrong.\u003C/p>\u003Cp>Speaker 1: So but, again, we support, you know, on some level. Right? Whether this is TinyMC or TipTap or name your editor. People have built their own interfaces here. There's a number of marketplace interfaces around this where the underlying libraries actually are more than we're currently exposing.\u003C/p>\u003Cp>And the question is, can we could we have interfaces have more composability or more extensibility kind of pre exposed, that you could then adapt. You can when the users, you know, when the administrators are setting these up, you could have custom, menu options. Right? So if you could actually I I know tinymc supports it because we've done it. Right?\u003C/p>\u003Cp>We've got the image and the video editor buttons that are there, and they inspect the appropriate tags. I think Tiny MC and Tiptap both support this ability to have an override or a templating option where you can adjust the configuration, add new buttons, add new drop down.\u003C/p>\u003Cp>Speaker 0: Oh, sure. Yeah. We could we could support that for, you know, file insert as well. If we have an insert button that we say, oh, if you're inserting a type of a PDF or something that we, you know, we render it in a different way. It's just so much setup that you end up having to do.\u003C/p>\u003Cp>Right? So right now, indirect is you would have to do that per field, which will be kind of annoying. So we would have to add some sort of global defaults as well for for interfaces to make that a little bit more editable.\u003C/p>\u003Cp>Speaker 1: Yep. And that's why, again, I think some of that we're actually looking at. It's for the extensions team, the the directus labs team to take a look at actually doing some tests on creating some new interfaces or modifying faces Yeah. To give us an idea of what's possible, what would what would work well, and then we can decide down the line whether we adapt that and roll that back into the core interface options as well. Yeah.\u003C/p>\u003Cp>But\u003C/p>\u003Cp>Speaker 0: Or or use a block editor. Like, there's there's, of course, you know, when it comes to and this is this is huge breaking changes and that's something we will do anytime soon, but there's also the argument to be made that is maybe we shouldn't be dealing with HTML at all. I I know it's it's the sort of easiest path to website, but from a data format perspective and for a data portability perspective and a migration perspective and, using it in non web platforms perspective, it is objectively not the right tool for the job, I think.\u003C/p>\u003Cp>Speaker 2: Yep. Yeah.\u003C/p>\u003Cp>Speaker 1: Yep.\u003C/p>\u003Cp>Speaker 0: I think markdown is slightly better. You still have to render it out yourself and you still have some of these migration problems, but at least, you know, renderers for markdown exist across, you know, output types, iOS development, web development, etcetera. But as far as I'm concerned, July 2024, you know, a block editor that returns it in in JSON is the most friendly way to actually render it out across different output vendors. Right? So there's also a world where we can say, maybe we don't concern ourselves with an HTML editor at all.\u003C/p>\u003Cp>We really focus on having one really good block editor, be it based on, tip- tab, shout out to chat, or or anything else, and then offer some utilities to have that transform to HTML, just to make your life a little bit easier, but not try to save it and manage it as HTML within directness.\u003C/p>\u003Cp>Speaker 1: So like an on the fly, the API endpoint would dynamically transform the block editor JSON format into HTML as an option parameter.\u003C/p>\u003Cp>Speaker 2: Right.\u003C/p>\u003Cp>Speaker 1: Clever. Clever.\u003C/p>\u003Cp>Speaker 0: Because that way we get around a lot of these these issues effectively all of them by just not dealing with HTML.\u003C/p>\u003Cp>Speaker 1: No.\u003C/p>\u003Cp>Speaker 2: Are there, folks are doing it as well to provide you with JavaScript libraries to convert to HTML. It doesn't even need to be server side. Though server side seems like a cool option, but, there are libraries out there for converting, like, adjacent tree to whatever, and you can have custom elements. You can render them customly within your frameworks, all of that. I've done that personally as well.\u003C/p>\u003Cp>Like, I've worked with abstract documents as a JSON and rendered them in different frameworks with different components, all of that. And it just makes it a lot nicer to handle if you're willing to spend the effort. But that is kind of the catch of it. Right? HTML is just super easy, but it doesn't give you all the nice options which you would have if you dealt with it in a more advanced way.\u003C/p>\u003Cp>Yeah. But probably it's the reason why we have Wizzy Week in the first place But it's it leaves you with certain limits. Yeah. It leaves you with certain limits.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. No. I've got people that are, you know, they they\u003C/p>\u003Cp>Speaker 2: Which makes me always wonder, like\u003C/p>\u003Cp>Speaker 1: Go ahead.\u003C/p>\u003Cp>Speaker 2: No. Go for it.\u003C/p>\u003Cp>Speaker 1: No. I was just saying I've seen people copying and pasting in Wizzy, you know, HTML from other sources and breaking their actual web page.\u003C/p>\u003Cp>Speaker 0: Oh, yeah.\u003C/p>\u003Cp>Speaker 1: Because they've got CSS and other things that they're trying to override, and it's being overridden. And they're complaining that, oh, my fonts don't match on this thing that I copied and pasted from some third party source. Right? Think Oh,\u003C/p>\u003Cp>Speaker 0: that's a big thing. Work. Oh.\u003C/p>\u003Cp>Speaker 1: Yeah. It's I I actually dealt with this just recently for one of our and for one of our clients, one of our actually, a a legacy Ranger Studios client, that we still kind of help out and monitor and support their site. And they came complaining that the fonts weren't matching on articles that they were that they just published. And they said, sometimes it works and sometimes it doesn't. And I was initially, I'm thinking there's some I'm digging through\u003C/p>\u003Cp>Speaker 2: Some of the code on the site. Google Docs and sometimes go into And\u003C/p>\u003Cp>Speaker 1: and then I'm looking at the Wizzio, and then I happen I'm like, what are all these spans and these classes don't exist in our code.\u003C/p>\u003Cp>Speaker 0: What are yeah. And\u003C/p>\u003Cp>Speaker 1: then I real and then I started removing so and suddenly the formatting for the correct. It was picking up the correct font, but they were it was this font code for this HTML was overriding all of the underlying CSS that was configured for the site. So yeah. Yeah. It's a nightmare.\u003C/p>\u003Cp>Speaker 2: Okay. That leaves us with one more feature request after this feature request review, which is how do we get shareable, like, permission permissions baked into an asset link, which makes it easier to manage permissions for anything you link in your editor. And with the agreement that it's probably cool idea and a good idea to include a, a, an, button form, whatever in there that allows you to create an 8 anchor tag to any asset in directors.\u003C/p>\u003Cp>Speaker 0: Yeah. I think that would be a great one to have. And to your point, that's a new feature of choice.\u003C/p>\u003Cp>Speaker 2: Have anyone in the audience who wanna wants to tag on to that.\u003C/p>\u003Cp>Speaker 0: Now the good news is of this session is that all of these things that we described so far, they're all non blockers or supporting the MVP of this, which is insert link to file, which is fantastic, which means that I wanna say, okay. Let's just do it. Let's just do it. It's improvement. It's a new feature, but small one, you know, PRs are welcome.\u003C/p>\u003Cp>It's a good first issue. Let's just do it. Like, the the minimum viable of this? Sure.\u003C/p>\u003Cp>Speaker 2: Is it something we only wanna do in the week, or is it also something we wanna support in the\u003C/p>\u003Cp>Speaker 0: That is a good question. I don't think it's relevant for the block editor because, a file is filed. You can render it out as a link or an image however you want for the markdown interface though. I think it's relevant.\u003C/p>\u003Cp>Speaker 1: Yeah. Because currently, it's just a URL link. Right? You've got the add in an image. I don't see do we support video in markdown?\u003C/p>\u003Cp>I There's markdown, and that's\u003C/p>\u003Cp>Speaker 0: There's really no such yeah. There's there's no native markdown for video.\u003C/p>\u003Cp>Speaker 1: K.\u003C/p>\u003Cp>Speaker 0: So not really. There's also no HTML attributes in markdown. There's a lot of things that are in markdown.\u003C/p>\u003Cp>Speaker 1: That's okay. So, technically, we just need the file link. I'm putting a note in the ticket here in the in the discussion here. Go ahead. Go on this.\u003C/p>\u003Cp>Speaker 2: Alright. Same question for the block editor. Is it also something where we wanna support it as well? Since we might want feature parity between between all of our editor interfaces.\u003C/p>\u003Cp>Speaker 1: Agree.\u003C/p>\u003Cp>Speaker 2: I don't even know what the block editor is capable of doing right now.\u003C/p>\u003Cp>Speaker 1: So it's got image.\u003C/p>\u003Cp>Speaker 2: Like we do image and attachment. What is attachment? Is is attachment already what we're trying to do?\u003C/p>\u003Cp>Speaker 0: I think it is. Yeah.\u003C/p>\u003Cp>Speaker 2: It might be. Yeah?\u003C/p>\u003Cp>Speaker 0: Yeah. I think it is.\u003C/p>\u003Cp>Speaker 1: Already is. Yep. So the only thing we because,\u003C/p>\u003Cp>Speaker 0: the block editor natively supports that. The the library that we use for that just has it. Yep. Yep. The Wizzy tiny MCE does not doesn't have a concept of that yet, so we have to edit edit ourselves.\u003C/p>\u003Cp>Speaker 1: Yep. Yeah. So you get select the file, and I guess the the one thing that I noticed is it doesn't have video. Should block editor have video? Or is it just file as a file as a file for these guys outside of the images that have captions?\u003C/p>\u003Cp>That's a good question. Let's see. Do I have video first deal in here? It's just in some video and see what the preview or window. I don't know what that just did.\u003C/p>\u003Cp>Oh, it downloaded. No. I have a duplicate copy of that. Lovely. So should it have so for parity, does does the block editor support video?\u003C/p>\u003Cp>Speaker 2: Seems like it's just an attachment. Right?\u003C/p>\u003Cp>Speaker 0: I think it's just an attachment.\u003C/p>\u003Cp>Speaker 1: It's just doing it as an attachment right now, but should it have a video interface the same as the HTML does? No. Maybe. Or a video blank. Just for, like, again, feature parity across the where where possible.\u003C/p>\u003Cp>Speaker 0: I think there's a separate plugin for editor JS, which is what we use in the for the block editor, but we'll have to try it out. Different feature request for a different day.\u003C/p>\u003Cp>Speaker 1: All good. So we already have file, so the only place that this needs to be done is. Markdown in HTML. Right?\u003C/p>\u003Cp>Speaker 2: And I think the block edit can just treat whatever attachment the way it sees fit.\u003C/p>\u003Cp>Speaker 1: I will put a note Just\u003C/p>\u003Cp>Speaker 2: for videos in the WEEZY week, do we just insert a video tag, or how does it work?\u003C/p>\u003Cp>Speaker 1: Let's have a look.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: I'll go test it.\u003C/p>\u003Cp>Speaker 2: Yep. K. That's what I figured. Wizardwig. Also, what kind of fan fancy collection is it that you have there, Jonathan?\u003C/p>\u003Cp>My test my test collections always have all 3 of the editors in there. You guys can actually show and hide it. That's cool.\u003C/p>\u003Cp>Speaker 1: Yeah. So I'm using the conditional rules. I use it for demos. So I have this in my Yeah.\u003C/p>\u003Cp>Speaker 2: Yeah. Yeah.\u003C/p>\u003Cp>Speaker 1: My CMS demo essentially where I've got a page a paragraph component. And then depending on, you know, if they're a WYSIWYG shop or they're a markdown shop or they're a block header, just being able to show off the different interfaces quickly and easily, but show and hide them. And it shows off the conditional rule capabilities of the app as well. So\u003C/p>\u003Cp>Speaker 0: It's cool. My my debugging instance is just one of each on every page.\u003C/p>\u003Cp>Speaker 1: Yeah. So so you do get a video player in Wizziweb. Yeah. It's yeah. I think\u003C/p>\u003Cp>Speaker 0: it's just a video tag. If you check out the raw HTML of that, it's it's literally Giant\u003C/p>\u003Cp>Speaker 2: kitchen sink collection. We everything in there.\u003C/p>\u003Cp>Speaker 1: Yep. I have including just a flat text area. So all of our kind\u003C/p>\u003Cp>Speaker 2: of Yeah. So it probably is a video. Yeah.\u003C/p>\u003Cp>Speaker 1: Yep. So it actually renders it with Under the hood. With video HTML tags accordingly. So the question is, does the block editor and or we know markdown doesn't support it, but does the block editor support something similar? Right now we're just inserting it as a file.\u003C/p>\u003Cp>But if the block editor technically supports video, it would be nice to have parity to have the equivalency where video renders nicely. So I'll just put a note.\u003C/p>\u003Cp>Speaker 0: Anyways, taking this back slightly more on track. This feature request as a whole, easy. I think it's easy easy to to accept it. That that's what I was going for. That's so much easy to implement necessarily.\u003C/p>\u003Cp>Easy to accept it. I think it's we should just do it. It's a good first issue. I think it's great for for PRs, if anybody wants to take a swing at this. I think there's a lot of new ideas and new feature requests that are worth discussing more.\u003C/p>\u003Cp>Like, I think this signed link approach to permissions is a very, very interesting one that will be very relevant for something like this. But with that being said, the eye on the clock and me also have a hard stop, unfortunately. I wanna say thank you very much for watching once more. We'll be back soon. Soon, Asia.\u003C/p>\u003Cp>It was a it was a minute since last time, but we'll be back, I believe in 2 weeks, and if not, soon. Make sure to check out this show on direct. Io/tv. Your request review. If you like doing this, somebody like having to get here.\u003C/p>","Welcome everybody once more. We're ready to rock. Welcome to request review. Daniel is out this week, so we got a very special guest for you here. Say hello. Hello. Hello. Good day. Thomas. Welcome to the welcome to the phone. Welcome to the having me. So today, we're gonna be chatting about yet another feature request. And this time, Jonathan, what have we pulled up? We have the lovely improving the way that files are injected in the WYSIWYG editors, is the discussion for today. I believe the extensions team may actually be taking this on behind the scenes in a in a short term solution. But for now, it'd be nice to understand the requirements, the the gotchas, the whys and wherefores, the way that current the way we currently add links and images and videos within a WYSIWYG field. So you can edit links, move your links, take a look at your links here. But according to the to the request, they'd like this to be a little more fully featured in the way that the tags and things are added around the HTML under the hood. It's my understanding. But I'm no front end expert, so let's see what we can figure out and decipher. Alright. So this I can feel it. This is not one of those where it feels like, oh, that that should just be one button. Right? That's easy enough to do. Alright. So starting at the basics and pulling it up, if you seem to look enough to decide over here, by the way, I just added the same thing up, but a little bit bigger. The goal here is basically just like you can put in an image, you wanna link to a file. Right? Easy enough, you think. Inserting a link to a file in its purest form shouldn't be too crazy because we know what the IDs of the file. We know where you would find it slash assets. It's the same as an image. Right? Mhmm. Just as with an image though, the question immediately becomes permissions? Big one. Right? The user that is inserting the file has permissions to see those files and then select it to be linked. But that doesn't mean that the person on the other end that is that's seeing this HTML rendered on a website or something also has the permissions to that same file. Right? And we've seen this before with images. Yeah. I was trying to say it's the same problem with images. Right? Because images share the same properties. Yeah. So the way we do it for images right now is we do have that option where you can sort of hard code in one of the the tokens if that's the way you wanna do it. You could, of course, make permissions a little leaner for reading images. Although that way, you know, it's a little bit more than permission set up. Not ideal. You can have people log in, of course, on your own front end and then use that for permissions. But it feels like even with the link to files, it might get a bit worse because at least with an image, you can immediately see if it doesn't work because you get a broken image. Right? Because you get a permissions issue. So as the person that's implementing it, it becomes a little bit more obvious what's going on. But if you just have a link and the link doesn't work for some, but it works for others, that's tricky. That's tricky. So I think as per usual for for those who haven't seen one of these before, the way this oftentimes works is that, you know, we take the smallest feature request that that is in here. The what is the the status? What is the minimum viable? Then convergently think about, okay, what scope does this live in? What are all the edge cases that we have to think about? What are the problems that will come up? And then sort of take it back down to, okay, what can we actually do today? Right? And what is the long term plan and how do we get there? So I think to me, an important one for to make this one work as expected is all around permissions. So I'm actually very glad that you're here, Thomas, because you just work a lot on permissions as part of this, v 11 release in the RC that is out now, in the GA coming up very soon. Any immediate thoughts on the permission set up for this? Especially with files, I do worry that giving read access to a lot of files like you suggested previously is a big foot gun for people because you might store private files. You might store exports from collections or something in your files and giving, like, real access to any of them feels wrong to do it as a blanket or something. That's like a blanket permission. We we, the best possible way would be to surface that information somehow. Like, how visible is a link or how visible is, like, something you link to, since even if you're testing out the link on your live preview or something, you still might be logged into your CMS behind the scenes. So for you, the link also works. So you have would have to test it in a private window or something. But then again, we could show you, hey. This is how the public what the public permissions for this file look like. Users might not be able to access it. On the other hand, we don't know about your setup. Right? So we don't know, are you actually just visiting those with the public public permissions? Or are you adding a token? Or are you doing some other stuff with it since we don't know really what is the implementation that relies on it. So it's hard to think about a catch all solution that would help people not shoot their foot. So right now with images, we do it. We basically let you do anything with it, and don't hold your hand while doing it. And I wonder if that is also the solution in this case. So just provide more functionality. But if stuff breaks on your end, you have to be aware of it. Seems could be a documentation issue, but then again, the people reading our documentation aren't actually the users using the product in the end maybe. Yeah. It's a tricky one. And and to your point of, like, where do you wanna put this education? Right. Yeah. And and to your point, we when we had this issue for images, the conclusion was, well, you know what? Let's just make all the options available and and put the burden of setup on the the end user to make sure that they're using it right instead of on us to to try to help you with it. It did this made me, think of another feature request that we I don't think we've discussed quite yet here, but it gets come up a couple of times in the past, which is the nested relational permissions, so to speak, where you could say, files are inaccessible by default, but you can allow file reads if they are being read through another relational field. So for example, you could say files are off limits, but if you look up an avatar through a user, you can still access that one avatar in some of its fields, but only if you go in relationally through a user. How does it work with the assets endpoint though? Right. That's where it gets interesting because I'm I'm just wondering coming from. Yeah. No. Exactly. I'm just curious if there is an angle to that, because it made me think of this where I'm like, oh, if you assign an image or an asset into a WYSIWYG blob, maybe we can have it rely on a similar idea of permissions where it's like, okay. The things you make available within, one of those fields can be read in a certain way, but not others. Again, just convergently thinking about it. Do you have any other issues? Sorry. Good. I I don't wanna distract you from a thought. So go ahead, Hans. The the issue I'm seeing with that is, again, like, accessing some data of that asset, totally fine. I can see that work, how it would be set up in permissions. I would be able to do, like, the relational reversal of permissions or something or for items. But since the asset endpoint doesn't know about it, we are still at some point where we why would this user be able to see this? Because then we would have to look up. Are there any permissions that could allow it? And so it gets it gets really complicated on the asset end point. Like, the actual, like, file permissions isn't too much of a problem, but all of the metadata, but the whole, assets itself isn't too trivial to figure out Yeah. Image for that one. Maybe. Again, divergently thinking about this. Maybe the, there's an in between where we can do kinda like what what s 3 supports and and some of those storage providers where you have, like, a signed link that is unique to where it was used. So we can know from the way the asset was accessed, what field in what collection it was accessed from. And then that way, we can do the relational permissions thingy instead of relying on having to search. Because to your point, if you open an asset and we have to search through every single field to see if there's any chance that you used it, that feels crazy. That feels insane. But if we make it a unique link that is unique to that collection of field combination, then that simplifies it a little bit. Because now we do know A lot of things. Where it was accessed from. Yeah. Which effectively could be something like an access token for that. Yeah. Effectively. So so it could be it's it's effectively just just a token with that connects the dots between you're trying to access this image or this asset through an HTML blob, that is on your website. And because we generated a custom link for it, we know what that blob is, and then we can also help you debug, you know, if your file got, is being accessed from sources that you didn't expect it to, we can show you. It's like, well, you got it read from your your HTML body and this and this article or something. Right? Interesting. I like it. That was my thinking as well was either inherit permissions for the item, but then as Hannes noted that the asset endpoint has no idea about that currently. Now that doesn't mean that we don't enhance or find a way, kind of a unique URL or a unique identifier that indicates where this is being used. We've actually seen this. There's another request out there about knowing what what files are associated with which items. Right? It's Right. Right. We we have this problem right now without writing some very complex hook logic that track tracks through a whole set of relationals. You know, we don't because that asset endpoint is kind of independent of all of that and files kind of live independent of all of that, The way that the relational is unless you're forcing relational reverse creation, which then makes your file through, you know, direct us files gets really ugly really fast that way. We have this it's a fairly common request that we see from clients of, I wanna know what my files are associated with. Right? I wanna be able to see that and understand that. So This is this is also an interesting topic in the whole security versus usability piece. Because on on from from a user experience and even a developer experience, if I have a, public HTML blob and I put an image in there, I kinda assume that that image is now also in that same public blob. Right? So I would assume it to work. At the same time, if you if if we were to make it that way where it's like, oh, whenever you put an image inside of a HTML thing, it's now public. You can accidentally expose a whole lot of stuff. Right? Because it's it's a little opaque under the hood. From a security perspective, it's a little tricky. Is it though? I guess again, it's really one of those, like, what are all the different use cases Directus is used in kind of problem. And, like, who are the actual end users and who has who has been educated about that problem, caveat, whatever we wanna call it. Because I I generally like the idea like, it doesn't really have anything to do with that, feature request in general since that seems a lot more low level. But I do like the general idea of having something like a unique or signed, asset URL, which can be used to conditionally allow access to something. And so generating something could be go for it. Sorry. No. No. I was just curious. So when you're generating, you know, like, this this input screen right here, so you'd have a unique URL that's being generated here, right, in some way, shape, or form, whether that's a tag on the end of the, you know, asset endpoint that gives you a question mark, you know, unique key. That has to be stored then. Is that stored then as part of the director's files or a relational to the director's files kind of a system table where we store those unique IDs? And not so much implementation. Just I I'm thinking about so you'd have that ID. But you'd also be able to see that here. Could you also or optionally ensure that you give, you know, an access token option here? So as an actual field input where you could supply an access token or even a default access token for as part of the admin configuration. Because once again, I don't necessarily want users putting in access tokens, but have a way for the administrators to get to apply an access token that has the appropriate permissions for the file. That is the key piece there. That last that last little snippet of the sentence is the most important crucially important part that has the appropriate permissions for the files. The foot gun with the excess token is that somebody just puts in their own users' access token, YOLO it, and then it works. But now you're technically exposing that all over the place, right? Yeah. Because once you're, you're putting that excess token in the image and HTML, it's just a query parameter. So therefore, wherever you render it, somebody can see it. You have to consider it public. See it. They can So what you're effectively doing is you're not using the public role for those images. You're just using a token that you then make public. Right? So that last part is why the sort of developer or user experience or both. I mean, just the experience as a whole for using the static tokens as way to get access to those asset in a link or an image or otherwise. There's a couple of food guns, right, that you gotta be aware of that people may or may not be. Because again, that's one of those things where, you know, if you explain it, that makes a lot of sense. But if you don't know and you just wrongly assume you could put in any access token, you can be in a in a world of trouble. Right? Absolutely. K. Which makes me wonder if that is something I haven't really thought about all the ash cases, but it's something probably we can solve by using a j signed JWT or something that just scopes it to that specific item in a collection, then we don't even have to store that in the database, assuming that you have access to that. Because then in the asset endpoint, we could check, hey, does the user like, this request actually have access to that item, which is signed in the JWT? And that way, we can verify the access. And if that basically overrides whatever, like, more restrictive permissions you have, you would be good to go only for that specific item where that image is linked in. Yeah. So we're basically saying Linked or whatever. Try to summarize that so I understand that we're basically saying if you attach an image or an a file, a general asset, any asset to an item in a WYSIWYG or relational interfaces, maybe even anywhere else. Well, relational interface, I guess not because then you just have a data point, not a file. But if you have an asset link inside of a Wizzywig or a block editor, some of those markdown, we use a signed link that contains what collection and item it came from. And then we can use the permissions for that item instead of the permission for files. So therefore, if you have read access to that HTML field, you defect don't have read access to the images within it. That feels like a very clean setup as far as I'm concerned. Again, probably also food counts with exposing files or people linking to files, which should be internal at that point. It's more of a shooting your own foot than more, of a case where we are trying to prevent it the best we can. Yeah. But that's, that's kind of already here anyway. Right? Because you can restrict the files and folders that this has access to. Right? In the in the actual WYSIWYG config, you can, I I think Do the root folder or something? It's what we do. Right? It should only be what this user has access to. Right? So if I add Add in general. Yeah. Yeah. For sure. If I add an image, you know, I can add or edit. I don't wanna do that. I wanna So your your condominiums and images. Yep. This is my my permissions restrict me to whatever I have access to, and so I shouldn't be able to technically insert something that I shouldn't be able to now. Could get a little more complex, but I like the that was my thinking and couple of notes that I took. I want a way to restrict the permissions to based on the item's permissions. Yep. So I like the change that we do to go with Amazon. Yep. It's interesting. And it's it also could be something that is just a tick mark or something in that dialogue, which can say, hey. Create a signed link or create an unsigned link because people might not want to have that additional check, but just generally make stuff public or something. But it could be an option either for the end user to decide or for the admin setting it up to decide. I feel like that is a good way of doing it also to introduce it in a non breaking way if you ever wanna introduce it in just a feature release. Judd's asking if we could display make it so that it's visible in the sense of you'd be able to see who has access to that data. This is not great. Can we do that? If if we're doing it with a token, probably not. No. But if we use, like, the scope JWT, would you be able to see so if I was in here looking at this, would I be able to see could you display here who has access? That seems crazy. Whoever has access to, like, whatever item you're inserting this into has access to that file in or something. So it's not something where we have either this user, like, harnesses you in your file or something, or or do they do, like, color eagle? Or I don't know. So I didn't at least with the route we're planning on going or considering going, that wouldn't be the case. Although, now I'm thinking about it. If you have the we use the permissions for the item for the asset endpoint, you still have a similar case where you could render the HTML somewhere for a person that's not logged in. Although, I guess, at that point, it's it's more expected. But you still Oh, you mean, like, service side rendering or something where you have Yeah. Token or, like, some service level token that actually does the rendering and you still break your links? Yep. I mean, at that point at that point, you wanna do like, you wanna manually track what is publicly available or something, I feel like. Yeah. I'm I'm just wondering because still divergently thinking what we can do to make this just very obvious in how it works, but still secure at the same time. Like, I'm also thinking an alternative, and this is just completely other idea. But if there is a native dedicated public folder in the file library, just a different root folder in the file library that is always public, you make it super obvious that, oh, if you wanna put a public image in your article, pull it from the public folder, right, or move it there first to make it public and then use it in your article. Now I'm wondering if that is way too CMS specific. It is. Although, I guess, yeah, public sharing of files, yes, no, yes, no, maybe. I don't know. Tricky. I know a lot of dams are all public by default, which is also a strange idea. Lot of what? Sorry. I know a a couple of dam providers that default to everything being public for ease of use and then you have the opposite where you can mark certain files as private instead of the other way around. But that is also I mean, we do allow you to conf to set that up. Right? We do allow you to set up permissions. Hey. You're allowed to access files which are located within this folder Yeah. Through public permissions, public policies, whatever you wanna do it with. So we already have that. It's just not there by default. We tried again, I'm kind of okay with that from, I I I don't think we have nobody complains about the ability to make something public. It's more so the one. The other thing we should make sure we come back to in this, is it's also about the way it's being embedded. I think the HTML coding that's actually being used, making it easier to con Yeah. That is something we've glanced over so far. Right? Because Yeah. We're more concerned. Security is to me the the first thing we should be thinking about. Yeah. Because for an image, it's kind of there's an obvious way of how to do it. But for an a tag, I mean, there are some standard attributes we wanna put in there. Yeah. But now the question is, like, what is a standard way of linking to something? It's probably an a tag, but is it something that we want to make configurable if use people are using it in service side reactor ending, if you're rendering whatever? Or is that so much out of scope that we say, if you're doing this, you better use the block editor and do your custom rendering pipeline or something or use markdown and go with that? Is it something where we wanna be really open to not rendering just an a tag, but something user defined? Because in the initial request, it looks like it's the requesting only a tags or link tags, whatever. I haven't really been able to see that in the request there. Yeah. Here. Well, that's an alternative. This is what they're doing as an alternative. They're doing some things strangely behind the scenes. Rendered as oh, I think it's a typo. The link should be rendered as an tag. So as a tag maybe in the first sentence or the first paragraph, the motivation. I think that's supposed to be as an as an anchor tag. Yeah. Yeah. So what is this considered currently when we're inserting? Because this is an h ref. Right? It's an image. Is it it's an anchor text. I mean, for for images, it's an image. And for for links, it's just an anchor, an a tag. But a link is not something where we directly allow linking to any records or something. Right? It's just a insert user. And it's just a link. Insert a link. Yep. Yeah. Yeah. Yeah. So so this feature request in its base in most basic form, it's just when I click add a link, allow me to choose a file instead of add a link. That's that's that's bare bones what this is saying. So the same way that we can do image the image and media because they're applying specific tags. But if you're just adding a tag, right, so if I just say I wanna, you you know, make this a link or if I'm just creating a link, I have to specify the URL. So what they want is the to a static file, you'd now have to type in mydirectors.com/asset/whatever the ID is. And and this is this in its simplest form, this is basically just saying, give me a link to a file. Give me a link to a file, same as when you're doing this, where you're inserting an image and you're searching. So they want the they want this regardless of image or anything else. Got it. It's basically the exact same flows in certain image except it pops in a link instead of an inline embed. So you'd have another you'd have essentially another option here that's, you know, insert a direct as file or insert a file. Right? And maybe the image library filters and searches. Got it. Yeah. Which means we immediately derailed this discussion by going, hey. What about permissions? Exactly. That's that's the beauty of these these sessions, Thomas. Welcome aboard. Welcome to our crazy party. I still like the idea of making this this better. Right? So making this more understandable and more usable. Yeah. Image images within WYSIWYG are are infinitely. Because I have another topic if you wanna just derail it a little bit more on this at the other the other way, which is migrations to and from different URLs. If you change the URL of your direct installation, this technically breaks Yeah. It's paint. Actually, I it happened on this very record. I popped this open in preparation for this this morning, and I was like, why is my image not showing up? And I right clicked and it was I'd actually migrated this from, another project, a while back. You see. And now my URL was just back in just this morning. Yep. So when I was looking at the, you know, the link here, I had to actually adjust this to my correct URL. So this is a this is a known issue when we're doing, like, data migrations and things. We've got, our support team has some scripts and things that we'll go through and find try to find and replace known values here. But it's a It's a tricky problem to solve because it's it's by definition, the WYSIWYG one is the trickiest because it is HTML. It's not a pro like a readable format necessarily. I mean, you know. It's something that is like easily solved in I don't know. Like, for example, our block editor or something, right? Right. Yeah. Image could just be nested in JSON or something you can even learn back. Yeah. Yeah. Because you're also dealing with an ID and not a full link. That helps. It's best case best case, it shouldn't even include the link. Like, I don't know what our block editor does, to be honest, but best case, it should just be a acid link or something that just includes the ID of that item you're interested in or that acid. Right. The tricky bit is if you plop that into a browser, nothing shows up. Yeah. It doesn't know what to do with it. Yeah. Yeah. For sure. So you, you have very tricky, tricky setup where that is that is always an issue. And and I'm more than happy because we we have a little bit of time like Lockheed here to to try to to noodle on some ideas for ways around that. Because, you know, some some more CMS specific platforms, they use, like, short codes or something within the HTML. Right? Where it's just like bracket asset ID this and then go figure it out in in post processing. That is an option, but that requires the user of the Custom. The output to do something, or we have to have some sort of custom post processing set up for some fields where we say, oh, if you have an HTML field, we'll search through short codes and and execute them. Interesting. Or Yeah. Because custom web components. Custom web components. We could provide a set of custom web components that is a director's image and that knows your CMS location and then just needs the ID, which is effectively what we're doing for Nuxt. Right? There is, like, a country like a Nuxt image module direct as Nuxt module, which is, like, the base URL, and then it just knows the image ID and figures the rest out. Yeah. So that could be an option for the WYSIWYG that it's like, hey, if you're using a custom image resolver or something, you could just insert the ID in the link. You can't use the face tag outside of the head, could you, in HTML? Oh, don't ask me. I don't think so. I think base is only in the head. The for that that's just I'm not just thinking HTML brain because you were saying it should just be, like, a relative link instead of a full URL. More or less, you were talking just the ID. But I'm like, if you make it slash assets slash the ID, then it will work within Directus presumably, but it will not in your own website. But a base tag in HTML says, you know, graphics every single link to everything with this domain, which could be work around for that. Right? Where you just have a base tag that says, okay, make the base tag, the direct installation, and then it would just work. But I think a base tag breaks every relative link on your Right. Right. Right. I was gonna say breaks every relative link in your whole package. That's what I'm saying. If only there was a locally scoped base tag where you can just say only within the main tag of my Dom is the base tag something else. But, yeah, I don't think that's gonna be a good solution for this. Custom web component is an interesting one too because it requires, you know, a bit of custom JavaScript that needs to happen on every single website that wants to use this, and it's just just a bit of yeah. It is one possibility. Yep. Yeah. I think, if if we if we rely on more of a short code, based approach with some post processing, then you make the migration between projects really lightweight, assuming there's a way to skip the post processing. And then the API output could still be the full URL, but we rely on a short code in the database. So therefore, the value that we store is just like bracket asset ID with all the parameters that you want, and then we generate those into images on the fly. But it's just a bit of a I don't know. It's It it doesn't feel like our responsibility necessarily. Yeah. That's kind of why I'm struggling. Same with, the public folder or something. That that feels too much like forcing one way of solving it on people. Yeah. You're not wrong. You're not wrong. Tricky bum. Tricky bum. Oh, one more thing related and related, maybe a bit of both. For images, we just plop in an image tag. Right? That's that's what what it does. Do we want to support any other file types that are in line? Because videos images are in line, but if you link to a PDF file or something, Do we want to It comes up. We we see that request as well as additional, like, inline viewers. Right? PDFs. Right. Docs, you know, spreadsheets, whatever. Being able to actually have a preview of what that looks like. The tricky thing with those things is that they're all custom front end frameworks or plugins or libraries or web components. It's not it's not a browser standard. So if we wanna go that route, we would have to have some sort of way where you can configure what markup it spits out if you insert a PDF. Because then you get to choose between, oh, when when I'm inserting a PDF, I'm gonna insert my little HTML markup that spits out a PDF preview or something instead of just the link. Yep. So there's that's that's another common thing that we see on those types of fields. Right? So if I go to paragraph here Which already screams use a freaking block editor or anything other than a wizarding question. I you're not wrong. You're not wrong. So but, again, we support, you know, on some level. Right? Whether this is TinyMC or TipTap or name your editor. People have built their own interfaces here. There's a number of marketplace interfaces around this where the underlying libraries actually are more than we're currently exposing. And the question is, can we could we have interfaces have more composability or more extensibility kind of pre exposed, that you could then adapt. You can when the users, you know, when the administrators are setting these up, you could have custom, menu options. Right? So if you could actually I I know tinymc supports it because we've done it. Right? We've got the image and the video editor buttons that are there, and they inspect the appropriate tags. I think Tiny MC and Tiptap both support this ability to have an override or a templating option where you can adjust the configuration, add new buttons, add new drop down. Oh, sure. Yeah. We could we could support that for, you know, file insert as well. If we have an insert button that we say, oh, if you're inserting a type of a PDF or something that we, you know, we render it in a different way. It's just so much setup that you end up having to do. Right? So right now, indirect is you would have to do that per field, which will be kind of annoying. So we would have to add some sort of global defaults as well for for interfaces to make that a little bit more editable. Yep. And that's why, again, I think some of that we're actually looking at. It's for the extensions team, the the directus labs team to take a look at actually doing some tests on creating some new interfaces or modifying faces Yeah. To give us an idea of what's possible, what would what would work well, and then we can decide down the line whether we adapt that and roll that back into the core interface options as well. Yeah. But Or or use a block editor. Like, there's there's, of course, you know, when it comes to and this is this is huge breaking changes and that's something we will do anytime soon, but there's also the argument to be made that is maybe we shouldn't be dealing with HTML at all. I I know it's it's the sort of easiest path to website, but from a data format perspective and for a data portability perspective and a migration perspective and, using it in non web platforms perspective, it is objectively not the right tool for the job, I think. Yep. Yeah. Yep. I think markdown is slightly better. You still have to render it out yourself and you still have some of these migration problems, but at least, you know, renderers for markdown exist across, you know, output types, iOS development, web development, etcetera. But as far as I'm concerned, July 2024, you know, a block editor that returns it in in JSON is the most friendly way to actually render it out across different output vendors. Right? So there's also a world where we can say, maybe we don't concern ourselves with an HTML editor at all. We really focus on having one really good block editor, be it based on, tip- tab, shout out to chat, or or anything else, and then offer some utilities to have that transform to HTML, just to make your life a little bit easier, but not try to save it and manage it as HTML within directness. So like an on the fly, the API endpoint would dynamically transform the block editor JSON format into HTML as an option parameter. Right. Clever. Clever. Because that way we get around a lot of these these issues effectively all of them by just not dealing with HTML. No. Are there, folks are doing it as well to provide you with JavaScript libraries to convert to HTML. It doesn't even need to be server side. Though server side seems like a cool option, but, there are libraries out there for converting, like, adjacent tree to whatever, and you can have custom elements. You can render them customly within your frameworks, all of that. I've done that personally as well. Like, I've worked with abstract documents as a JSON and rendered them in different frameworks with different components, all of that. And it just makes it a lot nicer to handle if you're willing to spend the effort. But that is kind of the catch of it. Right? HTML is just super easy, but it doesn't give you all the nice options which you would have if you dealt with it in a more advanced way. Yeah. But probably it's the reason why we have Wizzy Week in the first place But it's it leaves you with certain limits. Yeah. It leaves you with certain limits. Yeah. Yeah. No. I've got people that are, you know, they they Which makes me always wonder, like Go ahead. No. Go for it. No. I was just saying I've seen people copying and pasting in Wizzy, you know, HTML from other sources and breaking their actual web page. Oh, yeah. Because they've got CSS and other things that they're trying to override, and it's being overridden. And they're complaining that, oh, my fonts don't match on this thing that I copied and pasted from some third party source. Right? Think Oh, that's a big thing. Work. Oh. Yeah. It's I I actually dealt with this just recently for one of our and for one of our clients, one of our actually, a a legacy Ranger Studios client, that we still kind of help out and monitor and support their site. And they came complaining that the fonts weren't matching on articles that they were that they just published. And they said, sometimes it works and sometimes it doesn't. And I was initially, I'm thinking there's some I'm digging through Some of the code on the site. Google Docs and sometimes go into And and then I'm looking at the Wizzio, and then I happen I'm like, what are all these spans and these classes don't exist in our code. What are yeah. And then I real and then I started removing so and suddenly the formatting for the correct. It was picking up the correct font, but they were it was this font code for this HTML was overriding all of the underlying CSS that was configured for the site. So yeah. Yeah. It's a nightmare. Okay. That leaves us with one more feature request after this feature request review, which is how do we get shareable, like, permission permissions baked into an asset link, which makes it easier to manage permissions for anything you link in your editor. And with the agreement that it's probably cool idea and a good idea to include a, a, an, button form, whatever in there that allows you to create an 8 anchor tag to any asset in directors. Yeah. I think that would be a great one to have. And to your point, that's a new feature of choice. Have anyone in the audience who wanna wants to tag on to that. Now the good news is of this session is that all of these things that we described so far, they're all non blockers or supporting the MVP of this, which is insert link to file, which is fantastic, which means that I wanna say, okay. Let's just do it. Let's just do it. It's improvement. It's a new feature, but small one, you know, PRs are welcome. It's a good first issue. Let's just do it. Like, the the minimum viable of this? Sure. Is it something we only wanna do in the week, or is it also something we wanna support in the That is a good question. I don't think it's relevant for the block editor because, a file is filed. You can render it out as a link or an image however you want for the markdown interface though. I think it's relevant. Yeah. Because currently, it's just a URL link. Right? You've got the add in an image. I don't see do we support video in markdown? I There's markdown, and that's There's really no such yeah. There's there's no native markdown for video. K. So not really. There's also no HTML attributes in markdown. There's a lot of things that are in markdown. That's okay. So, technically, we just need the file link. I'm putting a note in the ticket here in the in the discussion here. Go ahead. Go on this. Alright. Same question for the block editor. Is it also something where we wanna support it as well? Since we might want feature parity between between all of our editor interfaces. Agree. I don't even know what the block editor is capable of doing right now. So it's got image. Like we do image and attachment. What is attachment? Is is attachment already what we're trying to do? I think it is. Yeah. It might be. Yeah? Yeah. I think it is. Already is. Yep. So the only thing we because, the block editor natively supports that. The the library that we use for that just has it. Yep. Yep. The Wizzy tiny MCE does not doesn't have a concept of that yet, so we have to edit edit ourselves. Yep. Yeah. So you get select the file, and I guess the the one thing that I noticed is it doesn't have video. Should block editor have video? Or is it just file as a file as a file for these guys outside of the images that have captions? That's a good question. Let's see. Do I have video first deal in here? It's just in some video and see what the preview or window. I don't know what that just did. Oh, it downloaded. No. I have a duplicate copy of that. Lovely. So should it have so for parity, does does the block editor support video? Seems like it's just an attachment. Right? I think it's just an attachment. It's just doing it as an attachment right now, but should it have a video interface the same as the HTML does? No. Maybe. Or a video blank. Just for, like, again, feature parity across the where where possible. I think there's a separate plugin for editor JS, which is what we use in the for the block editor, but we'll have to try it out. Different feature request for a different day. All good. So we already have file, so the only place that this needs to be done is. Markdown in HTML. Right? And I think the block edit can just treat whatever attachment the way it sees fit. I will put a note Just for videos in the WEEZY week, do we just insert a video tag, or how does it work? Let's have a look. Yeah. I'll go test it. Yep. K. That's what I figured. Wizardwig. Also, what kind of fan fancy collection is it that you have there, Jonathan? My test my test collections always have all 3 of the editors in there. You guys can actually show and hide it. That's cool. Yeah. So I'm using the conditional rules. I use it for demos. So I have this in my Yeah. Yeah. Yeah. My CMS demo essentially where I've got a page a paragraph component. And then depending on, you know, if they're a WYSIWYG shop or they're a markdown shop or they're a block header, just being able to show off the different interfaces quickly and easily, but show and hide them. And it shows off the conditional rule capabilities of the app as well. So It's cool. My my debugging instance is just one of each on every page. Yeah. So so you do get a video player in Wizziweb. Yeah. It's yeah. I think it's just a video tag. If you check out the raw HTML of that, it's it's literally Giant kitchen sink collection. We everything in there. Yep. I have including just a flat text area. So all of our kind of Yeah. So it probably is a video. Yeah. Yep. So it actually renders it with Under the hood. With video HTML tags accordingly. So the question is, does the block editor and or we know markdown doesn't support it, but does the block editor support something similar? Right now we're just inserting it as a file. But if the block editor technically supports video, it would be nice to have parity to have the equivalency where video renders nicely. So I'll just put a note. Anyways, taking this back slightly more on track. This feature request as a whole, easy. I think it's easy easy to to accept it. That that's what I was going for. That's so much easy to implement necessarily. Easy to accept it. I think it's we should just do it. It's a good first issue. I think it's great for for PRs, if anybody wants to take a swing at this. I think there's a lot of new ideas and new feature requests that are worth discussing more. Like, I think this signed link approach to permissions is a very, very interesting one that will be very relevant for something like this. But with that being said, the eye on the clock and me also have a hard stop, unfortunately. I wanna say thank you very much for watching once more. We'll be back soon. Soon, Asia. It was a it was a minute since last time, but we'll be back, I believe in 2 weeks, and if not, soon. Make sure to check out this show on direct. Io/tv. Your request review. If you like doing this, somebody like having to get here.","published",[139,150,160],{"people_id":140},{"id":141,"first_name":142,"last_name":143,"avatar":144,"bio":145,"links":146},"23ebcf2c-4374-4f5c-8198-f8ad497fd856","Rijk","van Zanten","7ef9652f-3835-432c-a43a-c5fe13001f31","CTO of Directus",[147],{"url":148,"service":149},"https://directus.io/team/rijk-van-zanten","website",{"people_id":151},{"id":152,"first_name":153,"last_name":154,"avatar":155,"bio":156,"links":157},"0d906492-75f0-45d9-abf7-ab779bf1ed08","Jonathan","Wagner","5062e4df-a198-4b40-af47-42362d3c0551","Sales Engineering Manager at Directus",[158],{"url":159,"service":149},"https://directus.io/team/jonathan-wagner",{"people_id":161},{"id":162,"first_name":163,"last_name":164,"avatar":165,"bio":166,"links":8},"e6b82950-9568-4d41-8b3b-c4e52cdb4957","Hannes","Küttner","137f1a82-7b66-400f-beb3-3fcee1823d7a","Engineer at Directus",[],{"id":169,"number":170,"year":171,"episodes":172,"show":185},"6aa046f1-bd53-4510-9af0-c0f3daaf4415",1,"2024",[173,174,175,176,177,178,179,180,122,181,182,183,184],"daed2c08-703a-43d6-ac97-aacac61be4c0","86fa152b-6a8b-477e-94b5-bd91e1202d21","0b5f4343-1494-455b-b41a-25811c151242","b2b01569-d8c6-49a7-adaa-429fe84f204f","b63afbe1-6418-4e9e-b1da-4890979789f0","69ad81e8-5e1d-4b85-9fa9-3b767a3a3478","5c9c888c-f527-4608-a2f7-56f156d00980","243daa59-3772-4ebe-b212-c2a09a4a0b71","12c8f72d-22fa-4ffa-a9d1-57047216fd1a","8896c934-aa2c-43b6-9342-8275682ab8b2","84c7b3ac-fd85-4539-8f39-3247118bcbf2","044b7c89-aaec-43b2-9d6d-6743a0fb5afd",{"title":186,"tile":187},"Request Review","73687d01-3734-4c28-aef7-e6fa8db4cf1e",{"title":8,"meta_description":8},{"id":181,"slug":190,"season":169,"vimeo_id":191,"description":192,"tile":193,"length":194,"resources":195,"people":8,"episode_number":198,"published":199,"title":200,"video_transcript_html":201,"video_transcript_text":202,"content":8,"seo":203,"status":137,"episode_people":204,"recommendations":208},"18618","1000292187","In this recording of our live event on August 15 2024, Daniel, Jonathan, and Rick discuss import and export options for flows","d71dfd7c-0662-41cc-aece-dfa8bc79919a",54,[196],{"name":130,"url":197},"https://github.com/directus/directus/discussions/18618",10,"2024-08-22","Import & Export option for Flows","\u003Cp>Speaker 0: Welcome to another episode of request review. What are we talking about today?\u003C/p>\u003Cp>Speaker 1: We're talking about import and export options for flows.\u003C/p>\u003Cp>Speaker 0: Oh. Now then if he says a like that, it sounds\u003C/p>\u003Cp>Speaker 2: Why don't we just edit then? You know? It should be quick fix. Right?\u003C/p>\u003Cp>Speaker 0: I think that's a great idea. Well, with that being said, thank you so much for watching. Find this episode on of course, I'm kidding. There's there's interesting, as per usual, these episodes, that's why we do them. Some fun edge cases here to consider.\u003C/p>\u003Cp>First and foremost, for those, you know, out of the loop flows, automation workflows, you can set up, you know, triggers. If somebody saves a thing, somebody hits an endpoint, somebody clicks a button in the app, do something. Right? And that something is a set of individual operations, different blocks, so to speak. You can connect together to make it do things.\u003C/p>\u003Cp>Now if you create multiple similar flows or you wanna move them to and from different installations, having some sort of way to import them and export them makes sense. Right? Just as a core set of functionality, absolutely. Somebody in the chat too. You know, flow and operation import is the number one thing that breaks when we sync our schemas and database across different projects.\u003C/p>\u003Cp>So why doesn't it not exist then, or why does it not work properly?\u003C/p>\u003Cp>Speaker 2: That's a good question. I think, that was before my time. So you're probably better equipped to answer that question.\u003C/p>\u003Cp>Speaker 1: There's there's a variety of things that happen here. Right? So schema differentials. Right? So if the scheme is different ahead of the flows being imported, if you're watching and monitoring things that don't exist in that import side, problematic, variables, referencing data and information that doesn't exist in one system to another.\u003C/p>\u003Cp>There's a flows are moving flows, I mean, the APIs are there. We do it all the time with our templating stuff, but we do that in a very controlled way where we're importing and handling the relationals. Because once again, now you've got relational data operations to the flows themselves and all of the data. What I've got a marketplace operation that's not installed in the next environment, there's, you know, there's a there's a great many things that have to be validated and checked, especially on import. Export, not so much, but on import, import gets\u003C/p>\u003Cp>Speaker 0: Absolutely. Has to be And then another important part of those same edge cases there is what about options that need to be different installation to installation, but, you know, 90% of the rest of it is the same. So for example, a a super simple flow that I oftentimes use as an example is, you know, you make a change and you trigger a build on your CD platform, like a Netlify build or something. That production Netlify URL is different project to project because you're building different websites, but the flow configuration, the trigger, you know, the setup for making the request and all that is the same. Cool.\u003C/p>\u003Cp>So looking at this discussion here on GitHub, basic example, motivation. Now the motivation makes a lot of sense. That's just basically, you know, what we're what we're talking about here. I think there's no no discussion on, the usefulness here. When it comes to actual implementation, there's a question of, do we tie this to part of the schema snapshot and apply logic?\u003C/p>\u003Cp>Right? Do we really treat this as it's part of your project's configuration, and we wanna move that between different instances? Or do we treat this more as a kinda more traditional import export like we have for regular collections where you can just say, well, give me all of the operations, and we'll reimport them back in.\u003C/p>\u003Cp>Speaker 2: To me, personally, it sounds more of the traditional way. Like, the correct way would be the traditional way because flows are not specifically schemas. Right? Like like, they're entries in the table, and that's not the schema, those would be items, and I think we should sync them separately. But yeah.\u003C/p>\u003Cp>Speaker 0: And immediately, people start chat typing in the chat. I love it. So this is this is a fun discussion that that has come up in the past in in these request review sessions anyways, but we're we're yet to reach the the the silver bullet answer for what is schema or what is configuration. Right? Flows is to me is one of those points where I think 80% of it is configuration of your project.\u003C/p>\u003Cp>And if you wanna duplicate your project or if you wanna move that from dev to prod or you wanna, you know, use it as a template starting point for something else, you can make the argument that it's definitely part of the same thing that is just one unit of export for the whole configuration of your project. Right? So that's schema and and flows. Last time we were talking about it, you know, it was in the context of roles and permissions, which is a similar but different, you know, part of data, part not. But to to just Josh's point here in the chat, in the data model, you know, the flows are just data in the database.\u003C/p>\u003Cp>But for the perspective of that end user, they're all part of the application that you're, you know, configuring, right, which makes sense. And in that case, you can definitely make the argument, oh, it's just part of, you know, the the application model settings, schema export, and and apply. But that still raises the question, is it the blanket everything? How do you subset it? Because for the schema right now, it's it's everything.\u003C/p>\u003Cp>And how do you deal with options that are different? And how do you deal with active versus inactive? Do we deactivate them on import by default just to be safe. Because you could have a cron thing that could go haywire and talk to, you know, 3rd party production systems if you just spin it up in a new thing.\u003C/p>\u003Cp>Speaker 2: Very valid points. And I and I do see the argument also from Joshua. It kinda it does make sense, right, if you interpret the flows as being part of your infrastructure, then they do kind of feel\u003C/p>\u003Cp>Speaker 0: schemey. Scheme.\u003C/p>\u003Cp>Speaker 2: I'm gonna use that. Scheme esque. Yeah. I I can see that argument. Okay.\u003C/p>\u003Cp>I'm kinda okay. Like in, in the beginning I was like, no, no, no, no. The data, they just stayed up. Those are rules. But now thinking about it, yeah.\u003C/p>\u003Cp>Speaker 0: It's the rules.\u003C/p>\u003Cp>Speaker 2: Damn. Interesting. Yeah. Kinda I'm kinda torn. I'm kinda in between.\u003C/p>\u003Cp>Like, it does it does make a hell of a lot of sense, but,\u003C/p>\u003Cp>Speaker 0: they got a lot. It's funny because we we keep finding ourselves coming back to the age old question of what is and isn't, project configuration that should be sort of version controlled is kinda the the where that's going. Right? Where you wanna have a file that you version control and that includes a bunch of stuff versus within the data, so you differentiate between the 2. And this is this is kinda what earlier when I said, oh, if I make if I build flow, the whole flow but I want the URL that we're talking to to be different.\u003C/p>\u003Cp>So that URL should be in the same static file export. Then by definition, it needs to be in this static file. Right? Because otherwise, you have to do both. Is that easier to maintain?\u003C/p>\u003Cp>Is that worse? Right? But if you're treating it as I wanna have a template that I use to create new projects, but then manage the rest in the project itself, that would still work. If you're treating it as the file becomes the source of truth, now you have to make duplicates of the same, you know, config files to make it work for your different projects. Might be a good thing.\u003C/p>\u003Cp>Might not be a good thing.\u003C/p>\u003Cp>Speaker 2: Did we did we actually, I think there's another small thing, related to this with, like, if if I just want to quickly export one specific one or, I had a not sure if this is, like, a slight tangent that we should go down on, but, when I played around with flows, I had the problem of, I created, like, 15 or 20 different ones, because I was, you know, trying it out and, they got automatically generated. And I had this little gripe with our way of displaying them because there was no, possibility to actually select multiple ones and delete multiple ones. And that kinda sounds to me like if we touch this, you know, with selecting them, exporting them, maybe, you know, filtering them so you only export a couple of them, it feels like selecting them would also fall or, like, lend itself to also be done with this issue. Right? But this is kinda a little different.\u003C/p>\u003Cp>Sorry. Just came to mind.\u003C/p>\u003Cp>Speaker 0: No. Yeah. You're absolutely right, though. Yeah.\u003C/p>\u003Cp>Speaker 2: Okay. So Joshua says, for a user story, we use tons of flows in production and development across 2 applications with the following workflow. We run the whole application locally, modify flows, code extensions. Okay. So far so good.\u003C/p>\u003Cp>Export the schema flows, collections, fields, etcetera etcetera, using a schema sync extension. Okay. So far so good. Commit the schema to version control and build images. Okay.\u003C/p>\u003Cp>In production, import the schema collections and flows as defined in the image. Alright. The result is that flows are always consistent between development and production, and the production flows are immutable. It makes sense. But this, I I guess this is to the point that I said earlier.\u003C/p>\u003Cp>Right? With then, you know, if you want to have, like, development flows production flows, then you have to duplicate them and both of them.\u003C/p>\u003Cp>Speaker 0: Here we go the route in the chat as well saying, you know, what I usually do is try to keep the flows clean by saying, the only thing I export is the values everywhere. And then for the parts where you use different day the data model, there's a sort of settings collection, I imagine, for those flow operations.\u003C/p>\u003Cp>Speaker 2: So you're actually fetching your needed stuff out of your database, and those fields would be different on your dev machine and the production environment. Correct?\u003C/p>\u003Cp>Speaker 0: Right. Yeah. It's basically the way to have that sort of 90%, 10%, you know, split I described earlier solved by using a a settings collection for that, which which makes me think, is that something that could be a native thing where at the top of whatever the file export is for flows, there is a set of very tools you can define, reuse within the file. So you have a single file source of truth that then the app, once you're importing it, the requests, what values do you wanna use for these variables to get food for thought. Right?\u003C/p>\u003Cp>For for those new in this session, it's always about divergently thinking through all of the options, and all of the edge cases and then trying to converge back to, okay, what is something we can do today versus the long term plan? That would be a kinda cool in between, I think, where, you know, in the static file, we have to make sure it's human readable and easy to manage, of course, but you can have you can just describe at the top of the file. Here's a couple of variables that I wanna be installation installation, and then reuse those in your operation settings. The moment you you know, from within Directus where you're using those flows, we can show a a sort of mini form in options that says, okay. What what are those variable values?\u003C/p>\u003Cp>Okay. Okay. Then I think one other thing in, in close that has been there's there's a couple of I mean, I don't know if it's a bug, but it's kind of annoying to deal with is that right now flows make an operations to tables. And it works right. So if you're using the current APIs, you have an export for operations expect you have an export for flows.\u003C/p>\u003Cp>Flows are sort of it's it's a one to many type of thing. Right? You have more 1 or more operations per flow. A very normalized SQL data model for that, which in its, you know, theoretical purity is correct. But it does also definitely mean that sometimes if you're importing operations where the flows doesn't exist, you get foreign key constraint problems.\u003C/p>\u003Cp>Right? Because you're pointing it to a flow that doesn't exist. It also means that when you're exporting it, you end up with 2 different export flows, one for the operations. So that is a bit of an order of insertion interesting thing. It also means that every operation points to the next operation it needs to trigger.\u003C/p>\u003Cp>So that also means that operation insertion order matters, in that table. And, again, that all makes sense from a SQL database perspective, but definitely not, you know, the most user friendly way to do it. And that is just, you know, the the thing you learn over time in in terms of the data model. The question actually came up just now. I see it in the chat, Joshua.\u003C/p>\u003Cp>Out of curiosity, you know, why is that a separate table instead of a big JSON field? Concerns about size and speed, script operations. The, it's partially because JSON fields were just not as well supported or existed at all. And at that point, it did become a bit of a, you know, performance concern in some databases. Luckily, you know, I know 2024, that is that is a different, picture now or, you know, even SQLite has support for for JSF, JSON fields, which is fantastic.\u003C/p>\u003Cp>Concerns about speed has been sort of resolved because of that because now databases can store them, you know, efficiently instead of the large text blob. Size is a concern still a little bit, and that is a similar thing where before it was just a text field, you know, databases were fairly inefficient around storing, you know, unbound blobs of text that would have been JSON but that should also now be way more doable across the SQL vendors. So for what it's worth, I actually think that now, especially now with SQLite support and Oracle DB also being on the JSON train, this is now a new question, right? Should operation just be a nested object on a flow? To which the answer is probably yes, actually, because it's always a tight coupling.\u003C/p>\u003Cp>You're not really moving an operation from one flow to another And storing the actual flow as a nested tree of operations is more efficient, so we don't have to stitch together that tree from from the c port rows. That was just the thing we couldn't do yet, you know, when we when we shipped flows, but nowadays, for sure. Yeah. So that is that is also an interesting thing, which helps with exporting and importing and helps with the ease of authoring it as a file. Right?\u003C/p>\u003Cp>Because if we're saying that operations are represented as a nested tree, kinda like, for those who are familiar with it, like CICD pipelines in GitHub or or those tools, it's oftentimes just a YAML file that says it runs step, step, step, step. Right? So a flow could be a very similar syntax in that sense if we store it that way.\u003C/p>\u003Cp>Speaker 2: Sounds reasonable.\u003C/p>\u003Cp>Speaker 0: And it's only a tiny method for I can change.\u003C/p>\u003Cp>Speaker 2: Okay. So we talked a little bit. How does it look like? How could it look like? Why would this feature be not as easy as you think?\u003C/p>\u003Cp>It's probably our standard go to.\u003C/p>\u003Cp>Speaker 0: So I think in its purest form, just export everything from one flow and import everything from one flow, either through a duplicate button or a separate endpoint. That is not super tricky. The insertion order when you're doing the import is a bit of technical complexity on the implementer side. But from a user experience perspective, it shouldn't be the end of the world. I think the real questions that we have to prepare ourselves for are around what happens if you have a flow that does something destructive and like a cron.\u003C/p>\u003Cp>And the moment you import it, it starts wreaking havoc. Right? Do we import them as disabled by default? Is that what you want? If you're in an environment where your prod is just importing it and and otherwise, it's considered immutable, you want them to be active instead of inactive.\u003C/p>\u003Cp>Right? So how do we do with deal with that? And then around that variableness of you have the flow that's the same, but you have some of the settings that need to be different. Is that something that we have to bake in, or is that something that we say, well, sucks. That just doesn't exist, and you have to duplicate files.\u003C/p>\u003Cp>I think it's worth at least thinking through those questions even if the initial implementation is just going to be, you know, almost the exact same what was described here, which is just import flows, export flows. Bang. Right?\u003C/p>\u003Cp>Speaker 2: According to Joshua, having a, destructive crone is just a skill issue. Fair enough. Problem problem fixed. One less problem to worry about. Alright.\u003C/p>\u003Cp>And, yeah, okay. So so the so the template ability, it it really does sound like a mustache template kinda thing. But, like, I'm I mean, we had a similar thing before, right, with, like, the script operation, for example, where it pulls in, like, your environment. So technically, the environment variables could be different. So you can actually have differing logic inside of them.\u003C/p>\u003Cp>Speaker 0: Yeah. And we do allow you know, in every operation, we allow you to use a value out of a previous step. So using, you know, the the suggestion that we set as a settings table is is a very good alternative, I think. Honestly, it makes a lot of sense. It makes a lot of sense.\u003C/p>\u003Cp>But that is something that we have to just say, okay. If that is our recommended approach, then let's document it as such, because we know it is a I I I mean, of course, we haven't done the the full surveys and everything else, but I would assume that this is a fairly common use case where, you know, the the main use story here is you have a dev environment, you export it so you have a single source of truth that's version controlled, and then you use that to power your production instance. Right? So you use the UI and everything else to configure it, and then you use the code as your immutable source of truth for prod. That's that's sort of the the first user story.\u003C/p>\u003Cp>And I think the second one is really that you have the export as a templated starting point or templated source of truth for multiple projects that are all in prod. Right? So either it is a starting point where if you spin up a new, you know, templated, the, event website or something and they're all the same, so you wanna use the same kickoff point. You have multiple production instances that use the same config. Right?\u003C/p>\u003Cp>That I think those are basically the main to, use cases for for this. Mhmm. And I think in both cases, there's a case to be made for, you know, template ability or at least the the variable, settings, which, again, table.\u003C/p>\u003Cp>Speaker 2: Table does sound pretty good. Pretty pretty reasonable. And similar to what you said, Rijk, earlier, or Jonathan rather, I think, the flows, according to Joshua, you know, if you import something, you could be referencing some tables that no longer exist. So how do we deal with that? Error messages, error pop ups, aborting, disabling the flow and just, you\u003C/p>\u003Cp>Speaker 0: know I think there's also a good data modeling question to that to be honest. I think for operations right now those settings that hold a a field or a collection name, those are basically just a string. Right? And then we, one once the operation runs, it uses that string to try to read data from those tables and whatnot. I think the only solution here is that we treat those as a separate data type.\u003C/p>\u003Cp>So instead of saying it's a string, we say it has to be a collection reference. And therefore, the moment you do an export or import, Directus as a platform can recognize what those settings are supposed to be and validate if they are valid paths. Yes or no. Right? So I I think the the the longer term answer to this question would be to treat those as different data types where it's like, okay, collection reference, field reference.\u003C/p>\u003Cp>So therefore, we can validate it on import or export or both, and almost do like a if you ever see it in in Word or Pages when you open the document, somebody had a bunch of custom fonts and it shows, you know, cannot use cannot find these fonts. Choose what you wanna use instead. That would be kinda the only way I can imagine that to work. Yeah? Mhmm.\u003C/p>\u003Cp>Speaker 2: I mean, if we, yeah, if we treat, you know, the, flows as part of your schema or your your infrastructure, your application, and if then an imported flow does not work because of, you know, like an an unreferenced or wrongly referenced thing in an operation, then technically we should reject that whole import because your entire application becomes inconsistent or can can could become inconsistent. So think, like, if we really consider it part of your application and your whole infrastructure, then we just have to reject everything if something goes wrong.\u003C/p>\u003Cp>Speaker 0: Hans makes a good point here in, in the chat saying, you know, same with role IDs in the permission settings. Effectively, what we're talking about is foreign key constraints, but in JSON, right, where it's like you have a a blob of settings that is different and unique to an operation type because every operation is different and we don't have like a we don't have individual tables for each of the operation types or something where that'd be insane. But what we're talking about here is effectively, how do we do forward key constraints within an operation here? Right? And exactly everything comes back to cache invalidation, naming things, and validating and structure data formats.\u003C/p>\u003Cp>That's great. Yeah. But some sort of way, you know, in the operation value, if we can read it statically, that'd be great, you know, where you can look at an operation settings value and just be recognize what are supposed to be form keys. It could be some sort of unique value syntax, although that feels a little proprietary. It could be, you know, that the operation defines, a sort of JSON schema type of thing that we know what that unstructured data type is supposed to be so we can validate it.\u003C/p>\u003Cp>But, yeah, at the end of the day, what we're talking about is really much foreign key constraints for, operational layout settings and everything else.\u003C/p>\u003Cp>Speaker 2: Yeah. Validation. Just to because I got curious, like, I'm, I think, just to getting back to the import order, like, please correct me if I'm wrong. So I think, if I remember correctly, the flow, references the first operation, and then each operation references the next operation. So the order of operate the the order of import would have to start with the first operate no.\u003C/p>\u003Cp>Because then you can't access you can't reference the second one. Oh, okay. So you have to do it backwards. Yeah.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah. Exactly.\u003C/p>\u003Cp>Speaker 2: Exactly. Exactly.\u003C/p>\u003Cp>Speaker 0: I'm still the father\u003C/p>\u003Cp>Speaker 2: to change.\u003C/p>\u003Cp>Speaker 0: Go on. It's the other way, bro.\u003C/p>\u003Cp>Speaker 2: Teacher, that's wrong. No. Right. I can see you have to step step through it once and then just pop off the stack and then go backwards. Okay.\u003C/p>\u003Cp>Speaker 0: Which is also why, you know, I mentioned if we save it as a nested object tree as is and just treat it as one big blob, right, instead of a semi structured thing, that that simplifies export import, like, tremendously, really, because we don't have to do that. We don't have to care about, insertion order because it's all nested on the flow. So, therefore, you don't have those foreign keys pointing back and forth. And a resolve and a reject just becomes a nested object instead of, what you call it, reference to a different, thing in the flat list. Right?\u003C/p>\u003Cp>So that simplifies quite a bit, in in that sense. It's a bit of a break and change for tooling that exists against the operations endpoint, but it would make this a lot a lot less annoying. And I think it also solves one other bug. We we've had a bug in flows where people have run-in into where if you're editing a flow, and I think it's you disconnect operation, then you try to reconnect it somewhere else\u003C/p>\u003Cp>Speaker 1: Right.\u003C/p>\u003Cp>Speaker 0: You get that error that is, like, cannot 4 giga straight. Right? Because you're, Yep.\u003C/p>\u003Cp>Speaker 1: You have to\u003C/p>\u003Cp>Speaker 0: disconnect it. You're dealing with 4 giga\u003C/p>\u003Cp>Speaker 1: from reconnect it in the order that you want. Yeah. It's a Yeah.\u003C/p>\u003Cp>Speaker 0: Exactly. And that is because you're trying to change those foreign key and how they point to each other in a way that is database technically speaking.\u003C/p>\u003Cp>Speaker 1: We have to answer that question for clients on\u003C/p>\u003Cp>Speaker 0: probably almost It's it's a it's theoretically correct. It's also annoying as hell. Yeah. It's one of those\u003C/p>\u003Cp>Speaker 2: Is neck beard correct? Actually actually, it is correct.\u003C/p>\u003Cp>Speaker 0: It is. Pure. It's database pure. Suck\u003C/p>\u003Cp>Speaker 2: it. Yeah. Oh, asks, are we talking about creating custom operations from the UI? No. We are actually talking about importing and exporting flows.\u003C/p>\u003Cp>So, for example, if you export your current schema and import your schema into your new environment, Should flows be included, how should how would that look like, etcetera etcetera.\u003C/p>\u003Cp>Speaker 1: So the template CLI utility that Alex and Bryant and the team put together, we migrate everything including flows. But we have full control over everything. It it's exporting content and schema and everything else comes back in in the appropriate order. So your schema, your, you know, updates and things that need to exist before flows get created all happens as part of that kind of operation steps through the CLI. So configuration as code, kind of the general thoughts that we're working towards there will help with this generally, but I I was taking notes here.\u003C/p>\u003Cp>I saw some of the comments over here. If you're updating an existing flow, Well, if you're updating something in production, flow migration is now gonna have to be maintenance window for anybody doing that. Right? Because if you've got active flows, you've got crons, you've got other things that are going on, if you suddenly come in and hammer a flow that made changes, you're gonna cause issues. You can break, you can lose data, cause problems.\u003C/p>\u003Cp>Transactional processing and other things can be impacted with updating an existing flow. So because currently, you know, our practice with the CLI tool is we delete all the operations and recreate them in order to avoid any kind of, you know, what changed or didn't change just from a complexity standpoint. But those are things that we're gonna have to consider as well as when you're migrating a flow. Are you updating the existing flow, or are you gonna delete and recreate?\u003C/p>\u003Cp>Speaker 0: Yeah. But,\u003C/p>\u003Cp>Speaker 1: you know, from an operational standpoint, my my alerts go off from a security risk perspective of if I'm using Flows for invoice processing or I'm using Flows as part of a payment process, which we have clients doing, And we're suddenly gonna, you know, whack a flow and recreate it, or we're gonna update it in some way. What are the operational impacts and risks of doing that? And they've gone crazy in the chat again.\u003C/p>\u003Cp>Speaker 0: To Hamza's point, that's kind of always a problem with with any sort of data migration. Right? If you import anything new into a production environment, you gotta make sure that you know what you're importing. That's that's the name of the game. That is also why, you know, the static file route in between is is what we're all talking about here.\u003C/p>\u003Cp>So that becomes your source of truth, and that is version controlled so you can see when it was updated by who. You can have some, you know, review processes in place to make sure that it has needs to have sign off, all that kind of stuff. But, yeah, it is technically always just a problem with any sort of data migration. If you have a settings table and import whatever on top of it, then it also breaks. Thinking about the settings table a little bit more, Are the settings always tied to a flow or is it more common to have settings that are global to your whole project and then happen to be reused in the flow?\u003C/p>\u003Cp>The reason why I'm saying that is in in a flow execution, we have that data object. Right? So that holds the all the data that's available in the operations and in the flow. One of the fields in there is that dollar sign trigger. That is just the information of the flow itself.\u003C/p>\u003Cp>Like, what caused it to trigger? I figured we could just have an additional flag in there for any sort of custom data that you wanna just have in that flow global, Right? That you can then reuse in those operations with the existing tools. So therefore, you can make a flow field, flow setting, Netlify URL and that just is becomes one of those data things that you can in in your request operation, you can use the parenthesis the the curly brackets, to reference back to your global flow data. Right?\u003C/p>\u003Cp>That could be a a sort of alternative to a settings table, but it would be unique to each individual flow. Yeah. Exactly what Joshua is saying right now. It's basically specific flow environment variables. Right?\u003C/p>\u003Cp>It would be, you know, environments in the, just that flow. And, yeah, you could theoretically already do that. You could use script operations or you could use the the JSON operation to just return, you know, some static data and then use that elsewhere. That's very true. Very true.\u003C/p>\u003Cp>But it could be a native thing. Then when you export and import it, you just have it as a sort of flow level thing, instead of as a separate operation.\u003C/p>\u003Cp>Speaker 2: Oh, I mean yeah. There's definitely both, both options. Like, having a global thing, is very useful for general stuff, like, even general info between operations, like a brand name, a URL that you reuse in between different actions. But I can also see, you know, the usefulness of tying something specifically to one, thing just for one thing. Sounds pretty good.\u003C/p>\u003Cp>I guess the the the, the actual question is just, okay, how do you manage that in a good way? You know, like, how do we make it pretty? How do we make it a good UX? But I yeah. It it makes sense from,\u003C/p>\u003Cp>Speaker 0: and One one I know Brian and Kevin will yell at me for if I don't bring it up. How do you save it encrypted? Like, some of those settings could very well be, you know, an API token or or things like that. Right? So how do you make sure that that stays secure as well?\u003C/p>\u003Cp>Speaker 1: Yep. That's why I I I think traditionally, most people seen here end up creating a custom table, that's an admin accessible only table. It's not given any permissions for anybody else. You can then store hashed keys. You can store data and values that are masked, only accessible to the administrators kinds of things.\u003C/p>\u003Cp>But I, you know, I've run into this. I actually had this report just a, like, a default language as a filter right across a flow. I was doing a bunch of flow operations and they were nested translations, and I wanted to be able to just say, oh, I only want a specific language. And then I was and you can do things like it's it's simple enough to do a run script that just exports the value. So now you've got that variable in memory, right, in in the data data payloads.\u003C/p>\u003Cp>But Right.\u003C/p>\u003Cp>Speaker 0: So That's that's where it gets that's oh, here comes another site engine. That's the fun thing about these episodes, man. I thought this is gonna be a 10 minute topic, and here we are 45 minutes in, and I just realized something new. When it comes to secret values like that, we have to store them encrypted, not hashed because we have to be able to decrypt them for use in the actual operation. Right?\u003C/p>\u003Cp>So when you make a request and you have to include some sort of token, it needs to be the original token, not the encrypted version. But to the chat's question here from Joshua again, you've been killing all of these questions. Thank you for them. You know, who's allowed to see the config? Right?\u003C/p>\u003Cp>Then, well, I'd say for the right UX at this, you can insert them once, and then that's it. Right? We don't show them again. But at the exact same time, you can then use a run script operation, return them, and then there they are. Right?\u003C/p>\u003Cp>So it's it's just a bit of a fake fake sense of security. They are encrypted into the database to make sure that that stays as secure as we can, so it will never be I mean, that's that's kinda this is kinda the crux of it. I it will never be exported as is, but at the same time, you know, does that matter? Even when you're doing it, it's not a closed closed.\u003C/p>\u003Cp>Speaker 2: Right? Yeah. Yeah. Especially since flows are, at least at least right now, just, an admin exclusive thing. So we kinda you know, like, security schmecurity.\u003C/p>\u003Cp>You know? Like, if you're already an admin, you you have different problems. Like, if your attacker is already an admin, your flow keys are the least of your worries, basically.\u003C/p>\u003Cp>Speaker 0: Yeah. Yes. No. Yes. No.\u003C/p>\u003Cp>Yes. No. Maybe. It's it's tricky in that sense. It's it's weird, though, when you think about it.\u003C/p>\u003Cp>And this is the same when you're talking from a platform team perspective, this is the same problem you're seeing on, you know, AWS secrets manager or d o environment variables or something like that. If you have an encrypted value and you save it, you then, you know, s h into your container, you do print f and there they are. Right? It's like they're stored as secure as possible, but then in your running process, you can just print the environment and everything is right there. But you gotta figure out what is what is the right move.\u003C/p>\u003Cp>Right? Because it can you have read only access to flows where they're hidden? But then if you have update access now, by definition, you have a way to expose them. Right? Is there a way is there a different security level where there should be the the sort of admins that have access to secure stuff and admins that can edit flows, but they're not the same thing?\u003C/p>\u003Cp>That that's a different question. Yeah. I\u003C/p>\u003Cp>Speaker 2: mean, if if we go down, you know, the the settings table route, then we already profit from our, authentication, authorization, system. So, you know, you you can already then assign oh, exclusively these people are allowed to read them, look at them, and stuff like that. So You don't need to plan out every case ahead of time. Well, then you're you're in the wrong stream, my friend. This this is what we do.\u003C/p>\u003Cp>Speaker 0: And again, this is this is the divergent part of all this. Right? This is luckily Yeah. A lot of these things are not blockers to at least get to the minimum viable, which is let us explore these damn things.\u003C/p>\u003Cp>Speaker 2: But Right.\u003C/p>\u003Cp>Speaker 0: These these are the things that people will, you know, use it for and maybe use it wrongly and then maybe expose themselves to massive headaches and issues down the line. So I do I do wanna at least be aware of the types of things that we might see in the future. So we can either prevent it in code, make it better, make it make it better down the line or document it in a way or just warn people about some of the dangers that they might hit. Right? It's, it's it's fun that you mentioned it, but at the same time, we've seen people do shit before.\u003C/p>\u003Cp>It's like, make a cron job that runs every second and put something in the table. Table blows up. Whose fault is that? Right? Did we did we have to do something to prevent that?\u003C/p>\u003Cp>Is that a user error? That who's who's responsible? Exactly. Featurex activity, see revisions. Working working on that, by the way.\u003C/p>\u003Cp>Unrelated for this call.\u003C/p>\u003Cp>Speaker 2: Don't tell. Don't tell. Hooray. Okay.\u003C/p>\u003Cp>Speaker 0: I don't want the sidetracked too much, but, yeah, we have the, retention settings for that coming, shipping fairly soon. Thank god. Yes, Amar. If you want to.\u003C/p>\u003Cp>Speaker 2: No. I\u003C/p>\u003Cp>Speaker 0: don't wanna get in trouble with HR. I don't think that's a good idea.\u003C/p>\u003Cp>Speaker 2: Goosebumps. Oh, no. Okay. So, just just keeping the time, in mind, we don't have that much time left. So maybe we should start to kinda converge now a little bit, You know, yeah, just to, get back on track a little bit.\u003C/p>\u003Cp>Oh, wow. And then the whole essay drops\u003C/p>\u003Cp>Speaker 0: from Yeah. There goes the next 10 minutes.\u003C/p>\u003Cp>Speaker 2: Oh, wow. Should we read that aloud? Are there bad words in it? Looks good. Okay.\u003C/p>\u003Cp>In my humble opinion, the whole edit complexity of environment migration I'm using directdis comes from the yaw application becoming data itself. Alright. While not using Directus, you can run different versions of the application side by side, and it's easy to switch version which version is running because the structure is defined within the application. Wildirectors, most of you. My guy, there there's not a single dot in that sentence.\u003C/p>\u003Cp>Oh my god. That's that's one sentence.\u003C/p>\u003Cp>Speaker 0: Don't throw too much shade. And it the the gist of it is because all the configuration is in the database instead of in code, therefore, you have migrations back and forth prompts. Right? And to Joshua's point, that is definitely a configuration as code discussion, which I think we had a couple episodes ago, actually, which is a great segue. Directs.i0/tv under request review.\u003C/p>\u003Cp>Highly recommend it. The the the 5 second summary of that discussion is really around there's multiple use cases. Right? There's a lot of people that prefer configuring everything from the UX and UI and then making an export. So by definition, it is in the database.\u003C/p>\u003Cp>Right? It's it's a mixed environment where there needs to be a two way binding into configuration. We don't wanna end up in a sort of again, the choices are different different use cases, different type of people. I'd I'd rather not end up with a sanity slash strappy type of environment where you have to pull things locally to code, you know, your settings and then redeploy it. That is just not quite the vision that we have for, you know, the ease of use and the user experience for this.\u003C/p>\u003Cp>Also, I know from some inside chatter that they're trying to get out of that as well. So this is this is a bit of a two way binding discussion.\u003C/p>\u003Cp>Speaker 2: Did you did you see They updated their comment and broke up the paragraph into multiple sentences now.\u003C/p>\u003Cp>Speaker 0: Danielle, look at what you've done. I think this is called cyberbullies. We don't condone that here.\u003C/p>\u003Cp>Speaker 2: Oh, no. Thank you for thank you for the, not not, what is it? Thank you for your cooperation, Wolfolas. Thanks for being here. Thanks for your message.\u003C/p>\u003Cp>We do appreciate it.\u003C/p>\u003Cp>Speaker 1: I'm not sure how you say it, but Wolf Wolf of us is an awesome contributor. He's been an incredible community member and contributor and helps out across everywhere across the platform. So\u003C/p>\u003Cp>Speaker 0: we are\u003C/p>\u003Cp>Speaker 2: It turns out.\u003C/p>\u003Cp>Speaker 0: Anyhoo, to your point then, converging it down to okay. What can we do? I think the the question that I do have is, do we wanna look into storing this as a nested JSON blob versus a separate table? Right? It would be a bit of a breaking change, but it simplifies both the output files and it simplifies the, the way to import it for us quite a bit.\u003C/p>\u003Cp>And it also solves that cannot resolve foreign key type thing bug in the same time. Right? The alternative is that we don't do that, but we avoid that breaking change in the operations endpoint with the downside that then for both export and import, we have to make sure that the whole nested tree is included at all times. And then during import, we do it in the correct insertion order. Right?\u003C/p>\u003Cp>Gut feeling wise, I'm kinda leaning towards let's blop it all as a nest of blob because it simplifies things. At the same time, couple downsides of that is that I think we'd lose the ability to choose what fields are returned from operations. I don't know if that matters as much if you're dealing with flows. Theoretically speaking, most of the databases now should support field selection for JSON. But seeing that we're talking about a theoretical infinitely deep object, that might be tricky.\u003C/p>\u003Cp>Right? Or we have to filter it down in in post after we got the data back.\u003C/p>\u003Cp>Speaker 2: Quick quick tangent. Can SQLite do that? I'm not sure. I don't think so. Right?\u003C/p>\u003Cp>Speaker 0: I think that update came earlier this year that they have some sort of SQL path, JSON path selection. Yeah.\u003C/p>\u003Cp>Speaker 2: They they've\u003C/p>\u003Cp>Speaker 1: got some JSON path.\u003C/p>\u003Cp>Speaker 0: Yeah. It's fairly recent. That's also one of the reasons why I missed we couldn't really do this earlier. Right?\u003C/p>\u003Cp>Speaker 2: Yeah. Oh, that's nice. Okay. Okay.\u003C/p>\u003Cp>Speaker 1: Yeah. So the lights are getting a lot of attention because you've got companies like Terso and others that are using this distributed edge file based databasing, caching kinds of things. So there's some there's a lot of work going on in that space and SQLite's gotten some pretty good attention.\u003C/p>\u003Cp>Speaker 0: But, I\u003C/p>\u003Cp>Speaker 1: mean, vendor variation, I think, as we move towards hopefully, as we move towards Duris, you know, whether or not that vendor supports it, then we can decide sourcing wise how we handle that. But for the main\u003C/p>\u003Cp>Speaker 2: I'm a big proponent of s Eskelet. Like, if if we if I do something, it it I wanted it for an SQLite, please. Okay. The end of this tangent. I'm sorry to I just I was curious.\u003C/p>\u003Cp>Yeah. Okay.\u003C/p>\u003Cp>Speaker 0: So so I think because, you know, the nested JSON stuff is something we haven't historically done because we couldn't really do that cross database vendor properly support it. It is something that probably comes with new issues that we don't know about yet. Right? So oh, yeah. Somebody actually mentioned it here too.\u003C/p>\u003Cp>It was like, Joshua again. Get a shout out, Josh. Stick with the current structure because making breaking breaking changes to the operation config will get to be a huge problem if using nested JSON objects. It's gonna be easier if you have a single row that changes. Just fair enough.\u003C/p>\u003Cp>And also see support for older databases and vendors that don't have JSON support. Also true.\u003C/p>\u003Cp>Speaker 2: Although,\u003C/p>\u003Cp>Speaker 0: I I wanna say that the ones that don't have an end of life I guess SQLite, the previous version, not yet, of course. Yeah. So and and, also, obviously, the big breaking change. People have been exporting, importing things through the APIs. The separate tools have been made.\u003C/p>\u003Cp>We saw that BCC schema sync shout out earlier. So we wanna make sure that that is not a huge freaking change either, right, where we just completely wreck any of those existing things. So sticking with the current structure probably makes the most sense. And then that raises the question around, you know, data integrity and import order, but that is a technical problem to solve that we just have to do during import. So that does mean that it'll probably be a separate way that we, do this than than compared to your usual export import just because order matters and that integrity matters.\u003C/p>\u003Cp>No. That's fine.\u003C/p>\u003Cp>Speaker 2: That should be really doable. You know? Walk the tree once and then just pop off from the back.\u003C/p>\u003Cp>Speaker 0: Yeah. And and the age old question, is this part of the schema export? Yes or no?\u003C/p>\u003Cp>Speaker 2: Oh, right. The the gonna\u003C/p>\u003Cp>Speaker 0: be a discussion for a different day, which is discussion we've had before around roles. And the yeah. But Joshua basically is concluding as well. We just need some schema export flags, which is, like, what do we include? What do we not include?\u003C/p>\u003Cp>This this this is the discussion we had exactly the same around roles. Do you wanna include roles and permissions? Probably. Do you wanna include users? Probably not.\u003C/p>\u003Cp>Do you wanna include permissions? Probably. Do you wanna include all of the permissions or just the ones that are about your production databases? Probably just the prod ones. How do you filter that down?\u003C/p>\u003Cp>This is the exact same stuff. Right? Is it all the flows? Is it some of the flows? How do you choose which of the flows are included and which ones aren't?\u003C/p>\u003Cp>Do we just export everything and then assume that somebody goes into the file and deletes them manually? Lots of more fun questions to be had in this. And for that, I wanna say, subscribe, like, and subscribe.\u003C/p>\u003Cp>Speaker 1: Nope. I think\u003C/p>\u003Cp>Speaker 2: Hit the bell. Hit the bell.\u003C/p>\u003Cp>Speaker 1: Always been.\u003C/p>\u003Cp>Speaker 0: Hit the bell and make sure you don't miss that episode when we go deep on that.\u003C/p>\u003Cp>Speaker 1: Now selective import export is gonna be needed for most of these things, I think. And ideally, whether export could export everything, but selective import on the import side, the ability to choose what I'm actually merging into the next iteration is is key.\u003C/p>\u003Cp>Speaker 2: Right.\u003C/p>\u003Cp>Speaker 0: Oh, that being said, I've not been muting. I'm muting. I'm muting. I'm muting. And I'm muting.\u003C/p>\u003Cp>The the doorbell is ringing, so I just wanted to make sure it didn't get too annoying. But that being said, we're at the top of the hour here. There's one more question that just came in. So another quick thought, what about pulling changes instead of pushing them in? Like, a sort of federation type of thing where you link data from multiple direct instances, you pull change from another instance.\u003C/p>\u003Cp>That sounds like a discussion for another day.\u003C/p>\u003Cp>Speaker 1: I think we've had that as part of the dual syncing as part of the configuration as code anyway. It is something that we we've we're we're thinking about, on that side of the house, and that's this is part of that as well. Right? So the configuration as code affects this, having utilities that make this easy now, versus, you know\u003C/p>\u003Cp>Speaker 0: Yeah. I I think that two way bind to me always has the file in between. So you push it from dev to file, and you pull it from prod from file. That is kind of the the the one to jump. Anyways, with that being said, thank you all for tuning in.\u003C/p>\u003Cp>Thank you all for the great questions and ideas in the chat. This episode will be available on Directus TV in the very near future. Shout out to shout out to Nat. Dan, did you make sure there's any any Zingers in here? Do we have the thumbnail?\u003C/p>\u003Cp>Speaker 2: Oh, that's not quite sure. May may maybe the the technically technically correct, maybe. I'm not sure.\u003C/p>\u003Cp>Speaker 0: There you go. That's that's the perfect one. That being said, check it out drex. Io/tv. We'll be doing this again in in the near future.\u003C/p>","Welcome to another episode of request review. What are we talking about today? We're talking about import and export options for flows. Oh. Now then if he says a like that, it sounds Why don't we just edit then? You know? It should be quick fix. Right? I think that's a great idea. Well, with that being said, thank you so much for watching. Find this episode on of course, I'm kidding. There's there's interesting, as per usual, these episodes, that's why we do them. Some fun edge cases here to consider. First and foremost, for those, you know, out of the loop flows, automation workflows, you can set up, you know, triggers. If somebody saves a thing, somebody hits an endpoint, somebody clicks a button in the app, do something. Right? And that something is a set of individual operations, different blocks, so to speak. You can connect together to make it do things. Now if you create multiple similar flows or you wanna move them to and from different installations, having some sort of way to import them and export them makes sense. Right? Just as a core set of functionality, absolutely. Somebody in the chat too. You know, flow and operation import is the number one thing that breaks when we sync our schemas and database across different projects. So why doesn't it not exist then, or why does it not work properly? That's a good question. I think, that was before my time. So you're probably better equipped to answer that question. There's there's a variety of things that happen here. Right? So schema differentials. Right? So if the scheme is different ahead of the flows being imported, if you're watching and monitoring things that don't exist in that import side, problematic, variables, referencing data and information that doesn't exist in one system to another. There's a flows are moving flows, I mean, the APIs are there. We do it all the time with our templating stuff, but we do that in a very controlled way where we're importing and handling the relationals. Because once again, now you've got relational data operations to the flows themselves and all of the data. What I've got a marketplace operation that's not installed in the next environment, there's, you know, there's a there's a great many things that have to be validated and checked, especially on import. Export, not so much, but on import, import gets Absolutely. Has to be And then another important part of those same edge cases there is what about options that need to be different installation to installation, but, you know, 90% of the rest of it is the same. So for example, a a super simple flow that I oftentimes use as an example is, you know, you make a change and you trigger a build on your CD platform, like a Netlify build or something. That production Netlify URL is different project to project because you're building different websites, but the flow configuration, the trigger, you know, the setup for making the request and all that is the same. Cool. So looking at this discussion here on GitHub, basic example, motivation. Now the motivation makes a lot of sense. That's just basically, you know, what we're what we're talking about here. I think there's no no discussion on, the usefulness here. When it comes to actual implementation, there's a question of, do we tie this to part of the schema snapshot and apply logic? Right? Do we really treat this as it's part of your project's configuration, and we wanna move that between different instances? Or do we treat this more as a kinda more traditional import export like we have for regular collections where you can just say, well, give me all of the operations, and we'll reimport them back in. To me, personally, it sounds more of the traditional way. Like, the correct way would be the traditional way because flows are not specifically schemas. Right? Like like, they're entries in the table, and that's not the schema, those would be items, and I think we should sync them separately. But yeah. And immediately, people start chat typing in the chat. I love it. So this is this is a fun discussion that that has come up in the past in in these request review sessions anyways, but we're we're yet to reach the the the silver bullet answer for what is schema or what is configuration. Right? Flows is to me is one of those points where I think 80% of it is configuration of your project. And if you wanna duplicate your project or if you wanna move that from dev to prod or you wanna, you know, use it as a template starting point for something else, you can make the argument that it's definitely part of the same thing that is just one unit of export for the whole configuration of your project. Right? So that's schema and and flows. Last time we were talking about it, you know, it was in the context of roles and permissions, which is a similar but different, you know, part of data, part not. But to to just Josh's point here in the chat, in the data model, you know, the flows are just data in the database. But for the perspective of that end user, they're all part of the application that you're, you know, configuring, right, which makes sense. And in that case, you can definitely make the argument, oh, it's just part of, you know, the the application model settings, schema export, and and apply. But that still raises the question, is it the blanket everything? How do you subset it? Because for the schema right now, it's it's everything. And how do you deal with options that are different? And how do you deal with active versus inactive? Do we deactivate them on import by default just to be safe. Because you could have a cron thing that could go haywire and talk to, you know, 3rd party production systems if you just spin it up in a new thing. Very valid points. And I and I do see the argument also from Joshua. It kinda it does make sense, right, if you interpret the flows as being part of your infrastructure, then they do kind of feel schemey. Scheme. I'm gonna use that. Scheme esque. Yeah. I I can see that argument. Okay. I'm kinda okay. Like in, in the beginning I was like, no, no, no, no. The data, they just stayed up. Those are rules. But now thinking about it, yeah. It's the rules. Damn. Interesting. Yeah. Kinda I'm kinda torn. I'm kinda in between. Like, it does it does make a hell of a lot of sense, but, they got a lot. It's funny because we we keep finding ourselves coming back to the age old question of what is and isn't, project configuration that should be sort of version controlled is kinda the the where that's going. Right? Where you wanna have a file that you version control and that includes a bunch of stuff versus within the data, so you differentiate between the 2. And this is this is kinda what earlier when I said, oh, if I make if I build flow, the whole flow but I want the URL that we're talking to to be different. So that URL should be in the same static file export. Then by definition, it needs to be in this static file. Right? Because otherwise, you have to do both. Is that easier to maintain? Is that worse? Right? But if you're treating it as I wanna have a template that I use to create new projects, but then manage the rest in the project itself, that would still work. If you're treating it as the file becomes the source of truth, now you have to make duplicates of the same, you know, config files to make it work for your different projects. Might be a good thing. Might not be a good thing. Did we did we actually, I think there's another small thing, related to this with, like, if if I just want to quickly export one specific one or, I had a not sure if this is, like, a slight tangent that we should go down on, but, when I played around with flows, I had the problem of, I created, like, 15 or 20 different ones, because I was, you know, trying it out and, they got automatically generated. And I had this little gripe with our way of displaying them because there was no, possibility to actually select multiple ones and delete multiple ones. And that kinda sounds to me like if we touch this, you know, with selecting them, exporting them, maybe, you know, filtering them so you only export a couple of them, it feels like selecting them would also fall or, like, lend itself to also be done with this issue. Right? But this is kinda a little different. Sorry. Just came to mind. No. Yeah. You're absolutely right, though. Yeah. Okay. So Joshua says, for a user story, we use tons of flows in production and development across 2 applications with the following workflow. We run the whole application locally, modify flows, code extensions. Okay. So far so good. Export the schema flows, collections, fields, etcetera etcetera, using a schema sync extension. Okay. So far so good. Commit the schema to version control and build images. Okay. In production, import the schema collections and flows as defined in the image. Alright. The result is that flows are always consistent between development and production, and the production flows are immutable. It makes sense. But this, I I guess this is to the point that I said earlier. Right? With then, you know, if you want to have, like, development flows production flows, then you have to duplicate them and both of them. Here we go the route in the chat as well saying, you know, what I usually do is try to keep the flows clean by saying, the only thing I export is the values everywhere. And then for the parts where you use different day the data model, there's a sort of settings collection, I imagine, for those flow operations. So you're actually fetching your needed stuff out of your database, and those fields would be different on your dev machine and the production environment. Correct? Right. Yeah. It's basically the way to have that sort of 90%, 10%, you know, split I described earlier solved by using a a settings collection for that, which which makes me think, is that something that could be a native thing where at the top of whatever the file export is for flows, there is a set of very tools you can define, reuse within the file. So you have a single file source of truth that then the app, once you're importing it, the requests, what values do you wanna use for these variables to get food for thought. Right? For for those new in this session, it's always about divergently thinking through all of the options, and all of the edge cases and then trying to converge back to, okay, what is something we can do today versus the long term plan? That would be a kinda cool in between, I think, where, you know, in the static file, we have to make sure it's human readable and easy to manage, of course, but you can have you can just describe at the top of the file. Here's a couple of variables that I wanna be installation installation, and then reuse those in your operation settings. The moment you you know, from within Directus where you're using those flows, we can show a a sort of mini form in options that says, okay. What what are those variable values? Okay. Okay. Then I think one other thing in, in close that has been there's there's a couple of I mean, I don't know if it's a bug, but it's kind of annoying to deal with is that right now flows make an operations to tables. And it works right. So if you're using the current APIs, you have an export for operations expect you have an export for flows. Flows are sort of it's it's a one to many type of thing. Right? You have more 1 or more operations per flow. A very normalized SQL data model for that, which in its, you know, theoretical purity is correct. But it does also definitely mean that sometimes if you're importing operations where the flows doesn't exist, you get foreign key constraint problems. Right? Because you're pointing it to a flow that doesn't exist. It also means that when you're exporting it, you end up with 2 different export flows, one for the operations. So that is a bit of an order of insertion interesting thing. It also means that every operation points to the next operation it needs to trigger. So that also means that operation insertion order matters, in that table. And, again, that all makes sense from a SQL database perspective, but definitely not, you know, the most user friendly way to do it. And that is just, you know, the the thing you learn over time in in terms of the data model. The question actually came up just now. I see it in the chat, Joshua. Out of curiosity, you know, why is that a separate table instead of a big JSON field? Concerns about size and speed, script operations. The, it's partially because JSON fields were just not as well supported or existed at all. And at that point, it did become a bit of a, you know, performance concern in some databases. Luckily, you know, I know 2024, that is that is a different, picture now or, you know, even SQLite has support for for JSF, JSON fields, which is fantastic. Concerns about speed has been sort of resolved because of that because now databases can store them, you know, efficiently instead of the large text blob. Size is a concern still a little bit, and that is a similar thing where before it was just a text field, you know, databases were fairly inefficient around storing, you know, unbound blobs of text that would have been JSON but that should also now be way more doable across the SQL vendors. So for what it's worth, I actually think that now, especially now with SQLite support and Oracle DB also being on the JSON train, this is now a new question, right? Should operation just be a nested object on a flow? To which the answer is probably yes, actually, because it's always a tight coupling. You're not really moving an operation from one flow to another And storing the actual flow as a nested tree of operations is more efficient, so we don't have to stitch together that tree from from the c port rows. That was just the thing we couldn't do yet, you know, when we when we shipped flows, but nowadays, for sure. Yeah. So that is that is also an interesting thing, which helps with exporting and importing and helps with the ease of authoring it as a file. Right? Because if we're saying that operations are represented as a nested tree, kinda like, for those who are familiar with it, like CICD pipelines in GitHub or or those tools, it's oftentimes just a YAML file that says it runs step, step, step, step. Right? So a flow could be a very similar syntax in that sense if we store it that way. Sounds reasonable. And it's only a tiny method for I can change. Okay. So we talked a little bit. How does it look like? How could it look like? Why would this feature be not as easy as you think? It's probably our standard go to. So I think in its purest form, just export everything from one flow and import everything from one flow, either through a duplicate button or a separate endpoint. That is not super tricky. The insertion order when you're doing the import is a bit of technical complexity on the implementer side. But from a user experience perspective, it shouldn't be the end of the world. I think the real questions that we have to prepare ourselves for are around what happens if you have a flow that does something destructive and like a cron. And the moment you import it, it starts wreaking havoc. Right? Do we import them as disabled by default? Is that what you want? If you're in an environment where your prod is just importing it and and otherwise, it's considered immutable, you want them to be active instead of inactive. Right? So how do we do with deal with that? And then around that variableness of you have the flow that's the same, but you have some of the settings that need to be different. Is that something that we have to bake in, or is that something that we say, well, sucks. That just doesn't exist, and you have to duplicate files. I think it's worth at least thinking through those questions even if the initial implementation is just going to be, you know, almost the exact same what was described here, which is just import flows, export flows. Bang. Right? According to Joshua, having a, destructive crone is just a skill issue. Fair enough. Problem problem fixed. One less problem to worry about. Alright. And, yeah, okay. So so the so the template ability, it it really does sound like a mustache template kinda thing. But, like, I'm I mean, we had a similar thing before, right, with, like, the script operation, for example, where it pulls in, like, your environment. So technically, the environment variables could be different. So you can actually have differing logic inside of them. Yeah. And we do allow you know, in every operation, we allow you to use a value out of a previous step. So using, you know, the the suggestion that we set as a settings table is is a very good alternative, I think. Honestly, it makes a lot of sense. It makes a lot of sense. But that is something that we have to just say, okay. If that is our recommended approach, then let's document it as such, because we know it is a I I I mean, of course, we haven't done the the full surveys and everything else, but I would assume that this is a fairly common use case where, you know, the the main use story here is you have a dev environment, you export it so you have a single source of truth that's version controlled, and then you use that to power your production instance. Right? So you use the UI and everything else to configure it, and then you use the code as your immutable source of truth for prod. That's that's sort of the the first user story. And I think the second one is really that you have the export as a templated starting point or templated source of truth for multiple projects that are all in prod. Right? So either it is a starting point where if you spin up a new, you know, templated, the, event website or something and they're all the same, so you wanna use the same kickoff point. You have multiple production instances that use the same config. Right? That I think those are basically the main to, use cases for for this. Mhmm. And I think in both cases, there's a case to be made for, you know, template ability or at least the the variable, settings, which, again, table. Table does sound pretty good. Pretty pretty reasonable. And similar to what you said, Rijk, earlier, or Jonathan rather, I think, the flows, according to Joshua, you know, if you import something, you could be referencing some tables that no longer exist. So how do we deal with that? Error messages, error pop ups, aborting, disabling the flow and just, you know I think there's also a good data modeling question to that to be honest. I think for operations right now those settings that hold a a field or a collection name, those are basically just a string. Right? And then we, one once the operation runs, it uses that string to try to read data from those tables and whatnot. I think the only solution here is that we treat those as a separate data type. So instead of saying it's a string, we say it has to be a collection reference. And therefore, the moment you do an export or import, Directus as a platform can recognize what those settings are supposed to be and validate if they are valid paths. Yes or no. Right? So I I think the the the longer term answer to this question would be to treat those as different data types where it's like, okay, collection reference, field reference. So therefore, we can validate it on import or export or both, and almost do like a if you ever see it in in Word or Pages when you open the document, somebody had a bunch of custom fonts and it shows, you know, cannot use cannot find these fonts. Choose what you wanna use instead. That would be kinda the only way I can imagine that to work. Yeah? Mhmm. I mean, if we, yeah, if we treat, you know, the, flows as part of your schema or your your infrastructure, your application, and if then an imported flow does not work because of, you know, like an an unreferenced or wrongly referenced thing in an operation, then technically we should reject that whole import because your entire application becomes inconsistent or can can could become inconsistent. So think, like, if we really consider it part of your application and your whole infrastructure, then we just have to reject everything if something goes wrong. Hans makes a good point here in, in the chat saying, you know, same with role IDs in the permission settings. Effectively, what we're talking about is foreign key constraints, but in JSON, right, where it's like you have a a blob of settings that is different and unique to an operation type because every operation is different and we don't have like a we don't have individual tables for each of the operation types or something where that'd be insane. But what we're talking about here is effectively, how do we do forward key constraints within an operation here? Right? And exactly everything comes back to cache invalidation, naming things, and validating and structure data formats. That's great. Yeah. But some sort of way, you know, in the operation value, if we can read it statically, that'd be great, you know, where you can look at an operation settings value and just be recognize what are supposed to be form keys. It could be some sort of unique value syntax, although that feels a little proprietary. It could be, you know, that the operation defines, a sort of JSON schema type of thing that we know what that unstructured data type is supposed to be so we can validate it. But, yeah, at the end of the day, what we're talking about is really much foreign key constraints for, operational layout settings and everything else. Yeah. Validation. Just to because I got curious, like, I'm, I think, just to getting back to the import order, like, please correct me if I'm wrong. So I think, if I remember correctly, the flow, references the first operation, and then each operation references the next operation. So the order of operate the the order of import would have to start with the first operate no. Because then you can't access you can't reference the second one. Oh, okay. So you have to do it backwards. Yeah. Yeah. Yeah. Yeah. Yeah. Exactly. Exactly. Exactly. I'm still the father to change. Go on. It's the other way, bro. Teacher, that's wrong. No. Right. I can see you have to step step through it once and then just pop off the stack and then go backwards. Okay. Which is also why, you know, I mentioned if we save it as a nested object tree as is and just treat it as one big blob, right, instead of a semi structured thing, that that simplifies export import, like, tremendously, really, because we don't have to do that. We don't have to care about, insertion order because it's all nested on the flow. So, therefore, you don't have those foreign keys pointing back and forth. And a resolve and a reject just becomes a nested object instead of, what you call it, reference to a different, thing in the flat list. Right? So that simplifies quite a bit, in in that sense. It's a bit of a break and change for tooling that exists against the operations endpoint, but it would make this a lot a lot less annoying. And I think it also solves one other bug. We we've had a bug in flows where people have run-in into where if you're editing a flow, and I think it's you disconnect operation, then you try to reconnect it somewhere else Right. You get that error that is, like, cannot 4 giga straight. Right? Because you're, Yep. You have to disconnect it. You're dealing with 4 giga from reconnect it in the order that you want. Yeah. It's a Yeah. Exactly. And that is because you're trying to change those foreign key and how they point to each other in a way that is database technically speaking. We have to answer that question for clients on probably almost It's it's a it's theoretically correct. It's also annoying as hell. Yeah. It's one of those Is neck beard correct? Actually actually, it is correct. It is. Pure. It's database pure. Suck it. Yeah. Oh, asks, are we talking about creating custom operations from the UI? No. We are actually talking about importing and exporting flows. So, for example, if you export your current schema and import your schema into your new environment, Should flows be included, how should how would that look like, etcetera etcetera. So the template CLI utility that Alex and Bryant and the team put together, we migrate everything including flows. But we have full control over everything. It it's exporting content and schema and everything else comes back in in the appropriate order. So your schema, your, you know, updates and things that need to exist before flows get created all happens as part of that kind of operation steps through the CLI. So configuration as code, kind of the general thoughts that we're working towards there will help with this generally, but I I was taking notes here. I saw some of the comments over here. If you're updating an existing flow, Well, if you're updating something in production, flow migration is now gonna have to be maintenance window for anybody doing that. Right? Because if you've got active flows, you've got crons, you've got other things that are going on, if you suddenly come in and hammer a flow that made changes, you're gonna cause issues. You can break, you can lose data, cause problems. Transactional processing and other things can be impacted with updating an existing flow. So because currently, you know, our practice with the CLI tool is we delete all the operations and recreate them in order to avoid any kind of, you know, what changed or didn't change just from a complexity standpoint. But those are things that we're gonna have to consider as well as when you're migrating a flow. Are you updating the existing flow, or are you gonna delete and recreate? Yeah. But, you know, from an operational standpoint, my my alerts go off from a security risk perspective of if I'm using Flows for invoice processing or I'm using Flows as part of a payment process, which we have clients doing, And we're suddenly gonna, you know, whack a flow and recreate it, or we're gonna update it in some way. What are the operational impacts and risks of doing that? And they've gone crazy in the chat again. To Hamza's point, that's kind of always a problem with with any sort of data migration. Right? If you import anything new into a production environment, you gotta make sure that you know what you're importing. That's that's the name of the game. That is also why, you know, the static file route in between is is what we're all talking about here. So that becomes your source of truth, and that is version controlled so you can see when it was updated by who. You can have some, you know, review processes in place to make sure that it has needs to have sign off, all that kind of stuff. But, yeah, it is technically always just a problem with any sort of data migration. If you have a settings table and import whatever on top of it, then it also breaks. Thinking about the settings table a little bit more, Are the settings always tied to a flow or is it more common to have settings that are global to your whole project and then happen to be reused in the flow? The reason why I'm saying that is in in a flow execution, we have that data object. Right? So that holds the all the data that's available in the operations and in the flow. One of the fields in there is that dollar sign trigger. That is just the information of the flow itself. Like, what caused it to trigger? I figured we could just have an additional flag in there for any sort of custom data that you wanna just have in that flow global, Right? That you can then reuse in those operations with the existing tools. So therefore, you can make a flow field, flow setting, Netlify URL and that just is becomes one of those data things that you can in in your request operation, you can use the parenthesis the the curly brackets, to reference back to your global flow data. Right? That could be a a sort of alternative to a settings table, but it would be unique to each individual flow. Yeah. Exactly what Joshua is saying right now. It's basically specific flow environment variables. Right? It would be, you know, environments in the, just that flow. And, yeah, you could theoretically already do that. You could use script operations or you could use the the JSON operation to just return, you know, some static data and then use that elsewhere. That's very true. Very true. But it could be a native thing. Then when you export and import it, you just have it as a sort of flow level thing, instead of as a separate operation. Oh, I mean yeah. There's definitely both, both options. Like, having a global thing, is very useful for general stuff, like, even general info between operations, like a brand name, a URL that you reuse in between different actions. But I can also see, you know, the usefulness of tying something specifically to one, thing just for one thing. Sounds pretty good. I guess the the the, the actual question is just, okay, how do you manage that in a good way? You know, like, how do we make it pretty? How do we make it a good UX? But I yeah. It it makes sense from, and One one I know Brian and Kevin will yell at me for if I don't bring it up. How do you save it encrypted? Like, some of those settings could very well be, you know, an API token or or things like that. Right? So how do you make sure that that stays secure as well? Yep. That's why I I I think traditionally, most people seen here end up creating a custom table, that's an admin accessible only table. It's not given any permissions for anybody else. You can then store hashed keys. You can store data and values that are masked, only accessible to the administrators kinds of things. But I, you know, I've run into this. I actually had this report just a, like, a default language as a filter right across a flow. I was doing a bunch of flow operations and they were nested translations, and I wanted to be able to just say, oh, I only want a specific language. And then I was and you can do things like it's it's simple enough to do a run script that just exports the value. So now you've got that variable in memory, right, in in the data data payloads. But Right. So That's that's where it gets that's oh, here comes another site engine. That's the fun thing about these episodes, man. I thought this is gonna be a 10 minute topic, and here we are 45 minutes in, and I just realized something new. When it comes to secret values like that, we have to store them encrypted, not hashed because we have to be able to decrypt them for use in the actual operation. Right? So when you make a request and you have to include some sort of token, it needs to be the original token, not the encrypted version. But to the chat's question here from Joshua again, you've been killing all of these questions. Thank you for them. You know, who's allowed to see the config? Right? Then, well, I'd say for the right UX at this, you can insert them once, and then that's it. Right? We don't show them again. But at the exact same time, you can then use a run script operation, return them, and then there they are. Right? So it's it's just a bit of a fake fake sense of security. They are encrypted into the database to make sure that that stays as secure as we can, so it will never be I mean, that's that's kinda this is kinda the crux of it. I it will never be exported as is, but at the same time, you know, does that matter? Even when you're doing it, it's not a closed closed. Right? Yeah. Yeah. Especially since flows are, at least at least right now, just, an admin exclusive thing. So we kinda you know, like, security schmecurity. You know? Like, if you're already an admin, you you have different problems. Like, if your attacker is already an admin, your flow keys are the least of your worries, basically. Yeah. Yes. No. Yes. No. Yes. No. Maybe. It's it's tricky in that sense. It's it's weird, though, when you think about it. And this is the same when you're talking from a platform team perspective, this is the same problem you're seeing on, you know, AWS secrets manager or d o environment variables or something like that. If you have an encrypted value and you save it, you then, you know, s h into your container, you do print f and there they are. Right? It's like they're stored as secure as possible, but then in your running process, you can just print the environment and everything is right there. But you gotta figure out what is what is the right move. Right? Because it can you have read only access to flows where they're hidden? But then if you have update access now, by definition, you have a way to expose them. Right? Is there a way is there a different security level where there should be the the sort of admins that have access to secure stuff and admins that can edit flows, but they're not the same thing? That that's a different question. Yeah. I mean, if if we go down, you know, the the settings table route, then we already profit from our, authentication, authorization, system. So, you know, you you can already then assign oh, exclusively these people are allowed to read them, look at them, and stuff like that. So You don't need to plan out every case ahead of time. Well, then you're you're in the wrong stream, my friend. This this is what we do. And again, this is this is the divergent part of all this. Right? This is luckily Yeah. A lot of these things are not blockers to at least get to the minimum viable, which is let us explore these damn things. But Right. These these are the things that people will, you know, use it for and maybe use it wrongly and then maybe expose themselves to massive headaches and issues down the line. So I do I do wanna at least be aware of the types of things that we might see in the future. So we can either prevent it in code, make it better, make it make it better down the line or document it in a way or just warn people about some of the dangers that they might hit. Right? It's, it's it's fun that you mentioned it, but at the same time, we've seen people do shit before. It's like, make a cron job that runs every second and put something in the table. Table blows up. Whose fault is that? Right? Did we did we have to do something to prevent that? Is that a user error? That who's who's responsible? Exactly. Featurex activity, see revisions. Working working on that, by the way. Unrelated for this call. Don't tell. Don't tell. Hooray. Okay. I don't want the sidetracked too much, but, yeah, we have the, retention settings for that coming, shipping fairly soon. Thank god. Yes, Amar. If you want to. No. I don't wanna get in trouble with HR. I don't think that's a good idea. Goosebumps. Oh, no. Okay. So, just just keeping the time, in mind, we don't have that much time left. So maybe we should start to kinda converge now a little bit, You know, yeah, just to, get back on track a little bit. Oh, wow. And then the whole essay drops from Yeah. There goes the next 10 minutes. Oh, wow. Should we read that aloud? Are there bad words in it? Looks good. Okay. In my humble opinion, the whole edit complexity of environment migration I'm using directdis comes from the yaw application becoming data itself. Alright. While not using Directus, you can run different versions of the application side by side, and it's easy to switch version which version is running because the structure is defined within the application. Wildirectors, most of you. My guy, there there's not a single dot in that sentence. Oh my god. That's that's one sentence. Don't throw too much shade. And it the the gist of it is because all the configuration is in the database instead of in code, therefore, you have migrations back and forth prompts. Right? And to Joshua's point, that is definitely a configuration as code discussion, which I think we had a couple episodes ago, actually, which is a great segue. Directs.i0/tv under request review. Highly recommend it. The the the 5 second summary of that discussion is really around there's multiple use cases. Right? There's a lot of people that prefer configuring everything from the UX and UI and then making an export. So by definition, it is in the database. Right? It's it's a mixed environment where there needs to be a two way binding into configuration. We don't wanna end up in a sort of again, the choices are different different use cases, different type of people. I'd I'd rather not end up with a sanity slash strappy type of environment where you have to pull things locally to code, you know, your settings and then redeploy it. That is just not quite the vision that we have for, you know, the ease of use and the user experience for this. Also, I know from some inside chatter that they're trying to get out of that as well. So this is this is a bit of a two way binding discussion. Did you did you see They updated their comment and broke up the paragraph into multiple sentences now. Danielle, look at what you've done. I think this is called cyberbullies. We don't condone that here. Oh, no. Thank you for thank you for the, not not, what is it? Thank you for your cooperation, Wolfolas. Thanks for being here. Thanks for your message. We do appreciate it. I'm not sure how you say it, but Wolf Wolf of us is an awesome contributor. He's been an incredible community member and contributor and helps out across everywhere across the platform. So we are It turns out. Anyhoo, to your point then, converging it down to okay. What can we do? I think the the question that I do have is, do we wanna look into storing this as a nested JSON blob versus a separate table? Right? It would be a bit of a breaking change, but it simplifies both the output files and it simplifies the, the way to import it for us quite a bit. And it also solves that cannot resolve foreign key type thing bug in the same time. Right? The alternative is that we don't do that, but we avoid that breaking change in the operations endpoint with the downside that then for both export and import, we have to make sure that the whole nested tree is included at all times. And then during import, we do it in the correct insertion order. Right? Gut feeling wise, I'm kinda leaning towards let's blop it all as a nest of blob because it simplifies things. At the same time, couple downsides of that is that I think we'd lose the ability to choose what fields are returned from operations. I don't know if that matters as much if you're dealing with flows. Theoretically speaking, most of the databases now should support field selection for JSON. But seeing that we're talking about a theoretical infinitely deep object, that might be tricky. Right? Or we have to filter it down in in post after we got the data back. Quick quick tangent. Can SQLite do that? I'm not sure. I don't think so. Right? I think that update came earlier this year that they have some sort of SQL path, JSON path selection. Yeah. They they've got some JSON path. Yeah. It's fairly recent. That's also one of the reasons why I missed we couldn't really do this earlier. Right? Yeah. Oh, that's nice. Okay. Okay. Yeah. So the lights are getting a lot of attention because you've got companies like Terso and others that are using this distributed edge file based databasing, caching kinds of things. So there's some there's a lot of work going on in that space and SQLite's gotten some pretty good attention. But, I mean, vendor variation, I think, as we move towards hopefully, as we move towards Duris, you know, whether or not that vendor supports it, then we can decide sourcing wise how we handle that. But for the main I'm a big proponent of s Eskelet. Like, if if we if I do something, it it I wanted it for an SQLite, please. Okay. The end of this tangent. I'm sorry to I just I was curious. Yeah. Okay. So so I think because, you know, the nested JSON stuff is something we haven't historically done because we couldn't really do that cross database vendor properly support it. It is something that probably comes with new issues that we don't know about yet. Right? So oh, yeah. Somebody actually mentioned it here too. It was like, Joshua again. Get a shout out, Josh. Stick with the current structure because making breaking breaking changes to the operation config will get to be a huge problem if using nested JSON objects. It's gonna be easier if you have a single row that changes. Just fair enough. And also see support for older databases and vendors that don't have JSON support. Also true. Although, I I wanna say that the ones that don't have an end of life I guess SQLite, the previous version, not yet, of course. Yeah. So and and, also, obviously, the big breaking change. People have been exporting, importing things through the APIs. The separate tools have been made. We saw that BCC schema sync shout out earlier. So we wanna make sure that that is not a huge freaking change either, right, where we just completely wreck any of those existing things. So sticking with the current structure probably makes the most sense. And then that raises the question around, you know, data integrity and import order, but that is a technical problem to solve that we just have to do during import. So that does mean that it'll probably be a separate way that we, do this than than compared to your usual export import just because order matters and that integrity matters. No. That's fine. That should be really doable. You know? Walk the tree once and then just pop off from the back. Yeah. And and the age old question, is this part of the schema export? Yes or no? Oh, right. The the gonna be a discussion for a different day, which is discussion we've had before around roles. And the yeah. But Joshua basically is concluding as well. We just need some schema export flags, which is, like, what do we include? What do we not include? This this this is the discussion we had exactly the same around roles. Do you wanna include roles and permissions? Probably. Do you wanna include users? Probably not. Do you wanna include permissions? Probably. Do you wanna include all of the permissions or just the ones that are about your production databases? Probably just the prod ones. How do you filter that down? This is the exact same stuff. Right? Is it all the flows? Is it some of the flows? How do you choose which of the flows are included and which ones aren't? Do we just export everything and then assume that somebody goes into the file and deletes them manually? Lots of more fun questions to be had in this. And for that, I wanna say, subscribe, like, and subscribe. Nope. I think Hit the bell. Hit the bell. Always been. Hit the bell and make sure you don't miss that episode when we go deep on that. Now selective import export is gonna be needed for most of these things, I think. And ideally, whether export could export everything, but selective import on the import side, the ability to choose what I'm actually merging into the next iteration is is key. Right. Oh, that being said, I've not been muting. I'm muting. I'm muting. I'm muting. And I'm muting. The the doorbell is ringing, so I just wanted to make sure it didn't get too annoying. But that being said, we're at the top of the hour here. There's one more question that just came in. So another quick thought, what about pulling changes instead of pushing them in? Like, a sort of federation type of thing where you link data from multiple direct instances, you pull change from another instance. That sounds like a discussion for another day. I think we've had that as part of the dual syncing as part of the configuration as code anyway. It is something that we we've we're we're thinking about, on that side of the house, and that's this is part of that as well. Right? So the configuration as code affects this, having utilities that make this easy now, versus, you know Yeah. I I think that two way bind to me always has the file in between. So you push it from dev to file, and you pull it from prod from file. That is kind of the the the one to jump. Anyways, with that being said, thank you all for tuning in. Thank you all for the great questions and ideas in the chat. This episode will be available on Directus TV in the very near future. Shout out to shout out to Nat. Dan, did you make sure there's any any Zingers in here? Do we have the thumbnail? Oh, that's not quite sure. May may maybe the the technically technically correct, maybe. I'm not sure. There you go. That's that's the perfect one. That being said, check it out drex. Io/tv. We'll be doing this again in in the near future.","c781a3e8-c119-41ef-a569-febf1970edb1",[205,206,207],"bb27ac7a-c413-4c5e-9fd0-0ea8bd9e86be","75add66e-c2ed-48da-a664-7a20c7da6a24","9d371e56-3e80-472c-abd3-22faf2a10c32",[],{"reps":210},[211,267],{"name":212,"sdr":8,"link":213,"countries":214,"states":216},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[215],"United States",[217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266],"Michigan","Indiana","Ohio","West Virginia","Kentucky","Virginia","Tennessee","North Carolina","South Carolina","Georgia","Florida","Alabama","Mississippi","New York","MI","IN","OH","WV","KY","VA","TN","NC","SC","GA","FL","AL","MS","NY","Connecticut","CT","Delaware","DE","Maine","ME","Maryland","MD","Massachusetts","MA","New Hampshire","NH","New Jersey","NJ","Pennsylvania","PA","Rhode Island","RI","Vermont","VT","Washington DC","DC",{"name":268,"link":269,"countries":270},"Michelle Riber","https://meetings.hubspot.com/mriber",[271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,248,459,460],"Albania","ALB","Algeria","DZA","Andorra","AND","Angola","AGO","Austria","AUT","Belgium","BEL","Benin","BEN","Bosnia and Herzegovina","BIH","Botswana","BWA","Bulgaria","BGR","Burkina Faso","BFA","Burundi","BDI","Cameroon","CMR","Cape Verde","CPV","Central African Republic","CAF","Chad","TCD","Comoros","COM","Côte d'Ivoire","CIV","Croatia","HRV","Czech Republic","CZE","Democratic Republic of Congo","COD","Denmark","DNK","Djibouti","DJI","Egypt","EGY","Equatorial Guinea","GNQ","Eritrea","ERI","Estonia","EST","Eswatini","SWZ","Ethiopia","ETH","Finland","FIN","France","FRA","Gabon","GAB","Gambia","GMB","Ghana","GHA","Greece","GRC","Guinea","GIN","Guinea-Bissau","GNB","Hungary","HUN","Iceland","ISL","Ireland","IRL","Italy","ITA","Kenya","KEN","Latvia","LVA","Lesotho","LSO","Liberia","LBR","Libya","LBY","Liechtenstein","LIE","Lithuania","LTU","Luxembourg","LUX","Madagascar","MDG","Malawi","MWI","Mali","MLI","Malta","MLT","Mauritania","MRT","Mauritius","MUS","Moldova","MDA","Monaco","MCO","Montenegro","MNE","Morocco","MAR","Mozambique","MOZ","Namibia","NAM","Niger","NER","Nigeria","NGA","North Macedonia","MKD","Norway","NOR","Poland","POL","Portugal","PRT","Republic of Congo","COG","Romania","ROU","Rwanda","RWA","San Marino","SMR","São Tomé and Príncipe","STP","Senegal","SEN","Serbia","SRB","Seychelles","SYC","Sierra Leone","SLE","Slovakia","SVK","Slovenia","SVN","Somalia","SOM","South Africa","ZAF","South Sudan","SSD","Spain","ESP","Sudan","SDN","Sweden","SWE","Tanzania","TZA","Togo","TGO","Tunisia","TUN","Uganda","UGA","United Kingdom","GBR","Vatican City","VAT","Zambia","ZMB","Zimbabwe","ZWE","UK","Germany","Netherlands","Switzerland","CH","NL",1773850451865]