Coldbox and VueJS untangled

Author: Wil (Page 1 of 6)

Quick create issues

Today one of my colleagues was working on some API project. He was creating some resources and just wanted to return the id of the value upon creation. So for example this call:

POST http://my.site/api/v1/company

should return the id of the newly created company. We are using the coldbox RestHandler and quick for database interaction, so we were expecting the following code would generate the desired result:

function create(event,rc,prc){
  //qCompany is a quick entity
  var oCompany = getInstance("qCompany").create({
    "name": "someCompany",
    "tenant_id": "1"
  });
  event.getResponse()
    .setData( { "id": oCompany.getId() } )
 	.setStatusCode( event.STATUS.CREATED )
	.addMessage( "Customer created" );   
}

Our VueJS frontend is expecting some integer number in the id data field. But to our surprise it was returning a string instead of a number. Not a big deal in some cases, but if you are more strict on your API you certainly want a number for an auto-incrementing key. So let me share some of the characteristics of our quick entity.

property name="id" column="company_id";
property name="name";
// db default for enabled = 1
property name="enabled" column ="enabled"; 
property name="tenant_id" column="tenant_id";

So nothing really special, an auto-incrementing id, some name, enabled which is 1 because of some db default and a tenant_id which is an integer number.

So what happens when you call getInstance("qCompany").create ? According to the docs there’s no need to call save()when calling create(), so I would assume we have an object with valid data now. But calling oCompany.getId() gives me a string instead of an integer, although it is a number? When entering data for my oCompany object I only entered a name and tenant_id, so let’s see what my object data looks like:

So I do have some Id, although it has the wrong datatype. The enabled property does not reflect the state in the database, it is empty. I entered my tenant_id as a string, so it is reflected here as a string. So it looks like it just showing us what we entered in our create function, except for the Id which just gets filled with some string.
Let’s see what happens if we create a second instance of this newly created item, by retrieving it from the database again. And as a final step we want to know if both instances are the same.

var myCompany = getInstance('qCompany@usermanagement')
  .create({
    "name":"someCompany",
    "tenant_id": "1" 
  });
var myCompany2 = getInstance('qCompany@usermanagement')
    .find(myCompany.getId());
writedump( 
  var = myCompany.issameAs(myCompany2), 
  label="sameAs"
);

The results:

Ok. So the enabled property is empty after creation, but if we retrieve the same entity from the database it has a value 1 as expected from our database default. Our Id has changed from string to a number and our tenant_id is also a number now. The funny thing is that the sameAs function thinks both instances are identical., although Id, tenant_id and enabled all return different data. In this case the function name promises more than the documentation which just tells us:

You can compare entities using the isSameAs and isNotSameAs methods. Each method takes another entity and returns true if the two objects represent the same entity.

So not exactly the same, but both representing the same entity. I guess that could give some confusion results if you are creating new entities. Luckily there is a very easy function to make sure sure it represents the data in the database: a simple call to myObject.refresh() will update all properties to the persisted values, but at the price of a database roundtrip.

I think at least the returned keyValue from getId() could use some better treatment in Quick. An auto-incrementing Key should never return a string if it is technically a number. Of course it is easy to fix by converting the ID to a number, changing the getId function or better yet, fix the auto-incrementing key in quick, of just create a new keyType which returns the correct data. We did a quick compare to Laravel, which just returns the correct datatype and value.
When comparing to cborm/cform the flexibility and ease-of-use in quick comes at a price. The handling of data types is a lot weaker, unless we start explicitly casting all properties in a quick entity. I did not test it yet, but I assume datatypes in cborm are more strict and it can also handle null values better.


Changing coldbox module behaviour without changing the module !

I have to admit, this title seems a little weird. How can I change some behaviour in a module without changing the code? And why do I want to change this behaviour?
Let me start with the why. I am using a lot of box modules, but sometimes there are some pieces missing, or am I not happy with some default behaviour. Many modules are very adaptable, for example using configuration settings or some interceptors. But sometimes this is not enough.
In a Free and Open Source Software world we just clone a repo, modify some code and send some pull request the the authors. But what if they don’t want your changes? I could fork the project, and create my own module, but from this moment on I am the maintainer of my own module. And sometimes other modules are depending on the module I want to fork, which is often not what I want. But there are other ways to change a module, and they work best for smaller changes. Let me explain.

Continue reading

How to get a visitors real IP in cfml.

Some of our clients love it when we log a lot of security related info in their applications. So on every authentication request we want to log the user’s IP and if we are denying access to some parts of the application we want to log this as well. So we have code like this all over our application:

authLogger.warn( "User #rc.username# from #getRealIp()# tried to do something dangerous" );
	

So can we detect the real IP of our users with high confidence?
The short answer: you can’t trace all the bad guys and people who want to stay anonymous, but for the majory of users you can get get some more info.

Continue reading

Logbox: automated archiving of your logs

When you start using Logbox the amount of logged messages can add up quickly, leading to huge logfiles or database tables with many many logEntries. Logbox has many different appender types, and in some cases size of your logs is controlled by external systems such as lucee or for example the logrotate system in Linux. If you are logging to a file you could use a rollingFileAppender which limits the file size, and for this appender you can configure the max number of file archives.

We prefer to use a DBAppender, so I can create some nice display of the logged details in a user interface, but for limiting the size and creating some archive it seems we are out of luck. According to the docs there are no options for limiting the size or creating some archive. So how are we going to fix that?

Continue reading

ImageNew bug in Lucee

Several months ago Eric Peterson published the totp module, a cfml implementation of Time-based One-time Password. More on TOTP in a future post, but to summarize: TOTP is used for 2-factor authentication, using some secret your app can work together with an external authenticator on a smartphone such as Google Authenticator, MS Authenticator, LastPass authenticator and many other authenticators. Your TOTP app should be able to generate some special url, and a QR code representing this URL, so configuring your authenticator can be as simple as scanning this QR code. The URL code and QR look like this:

QR code example

So for our qr code, the TOTP module needs some code to generate the bar code. And that’s exactly where Lucee failed.

Continue reading

Logbox: track your database changes (part 2)

In my previous post on tracking database changes I described how you can log all database changes for a quick orm system. I also mentioned you can do a similar thing for cborm systems which are based on coldfusion ORM (hibernate). I have used cborm a lot in the past, and even did some presentations on orm, but I noticed there are quite some people who don’t like or even hate the coldfusion orm, for being slow, memory consuming and hard to understand. I am not sure if this reputation is well deserved. Yes, error messages are hard to understand, but so are the errors in quick (if you are not the author of the module 🙂 . Initially Adobe did a bad job on default settings for ORM, which made it harder to understand and Lucee ruined my ORM setup when upgrading from lucee 4 to 5, and it took them several years to fix three lines of code. But nowadays, there is an update for the hibernate engine in Lucee, and more important: the cborm library does a very nice job in hiding the hard parts of database transactions in ORM, and it opens up the Criteria API from hibernate. Once you know how to handle cborm it is a lot faster than quick and it has a different abstraction for your entities. I will leave the details on performance for another post. Just want to mention both quick and cborm have their place, and in this post I will show you which steps you should take to track your database changes with cborm .

Continue reading

Cborm event handling: tracking some Lucee problems.

In my previous post on logging database changes with Logbox I wanted to show how to log database changes with interceptors in a cborm system. This should be quite simular to the quick example from the previous post. It just needs an extra step, you have to configure this in Application.cfc:

//configure this mapping here, your ormSetting need it
this.mappings[ "/cborm" ] = COLDBOX_APP_ROOT_PATH & "/modules/cborm";
this.ormSettings = {
    // ...... (other orm settings)
    // Enable event handling
    eventhandling = true,
    // Set the event handler to use, which will be inside our application.
    eventhandler = "cborm.models.EventHandler"
};

The cborm.models.EventHandler will act as a bridge between the coldfusion ORM event handling and the interceptor system in coldbox. Orm settings are configured in Application.cfc even before coldbox is loaded. I’ve been using this for many years in older application and it always worked like a charm. For my series of blogposts on Logbox I created up a new coldbox application, configured the ORM settings as I have done for many years, and fired up some recent Lucee version. It failed.

Continue reading

Logbox: track your database changes (part 1)

As a former owner and CTO of a webhosting company our support staff and our customers often made changes to DNS and email settings in our CFML customer portal, but this can be a dangerous affair if you don’t know what your are doing . Of course auditing can be done in the database systems, but in that case you’ll often miss specific information such as user ID’s, company names, ip numbers and so on. In other projects a detailed log of our database changes could also be very useful, so in this post I’ll discuss how you can log your changes very easily using Logbox and interceptors in a coldbox application.

Continue reading

Logbox: modify your message format.

In my previous post I explained some of the basics of Logbox, including the use of appenders. An appender is just a component which takes care of sending your log messages to some message repository, such as a file, console, socket, email, database and so on. Logbox is very handy because it has a standard format to send your log messages and optionally extra info. But sometimes you want to send extra information and show it in a nice format in your logs. Logbox has two ways to modify the output: Layout components and custom appenders.
In most cases Logbox is sending the following information

  • Message
  • ExtraInfo (any data)
  • Severity (from FATAL to DEBUG)
  • Category
  • Logdate
  • Appendername

Not all information is written to the target repository, this is depending on the appender. Some targets have their own logging dates, or they are missing some of the severities (like DEBUG). The DummyAppender is not even writing anything at all! So your appender determines what gets written and in what format.

Sometimes we want more. Most of my applications need authentication, so I would like to see the username in my logs. Other applications are multi-tenant, so I would like to see the tenant code (and the username) in my log. So how can I make sure my tenant code and username will be added to the LogEvent? And how can I write both properties to my log repository?

Continue reading

Logbox: basic concepts and configuration

As mentioned in my previous post it might be a bit overwhelming when you want to start logging with coldbox. So why not just appending some information to a file instead? There’s a few reasons for this:

  • standards: Logbox has a standard way of sending information to a logging source, so you don’t have to rewrite your code if your loggin requirements change. There are several logging levels (severity), more on that later. And configuration can be done in one central place if you want.
  • performance: Logbox has some standard async loggers. This is a performance benefit, your code will not wait until your logging is finished
  • reusability. This has a a lot in common with the first bullet on standards. All coldbox framework code and many libraries are using logbox. So if you know how to configure Logbox, you can tap into all info which will be logged by framework and other library code.
  • flexibility: Logbox allows you to enable debug logging or suppress other logging level for any part of your own code, the coldbox framework or installed modules. And it allows you to send debugging info to any repository you want. You can even create your own logging methods.

In this post I will show you how to add simple logging capabilities to a coldbox application. I will explain all concepts of Logbox in detail, but I will start with the short version:

  • create a Logbox struct in your coldbox configuration file with keys for appenders (required), root logger (also required), categories (optional but very useful) and the related keys debug, info, warning, error, fatal and off, which are all used to set maximum logging levels for certain categories
  • add at least one appender to your appenders array. There are many appenders such as file appenders, database appenders, console appenders and more. Each appender has at least a class name and some properties which may vary based on the type of appender
  • add a root logger. This root logger is some kind of default category. If you create a a named logger, which is a logger for a category it will try to find a logger configuration for this category and if it doesn’t find the category or a parent category it will use this root logger config. A root logger has an appenders property where you can specify to which appenders the message will be sent, and a levelMin (default=FATAL) and levelMax (default=DEBUG) property to specify which level of messages will be allowed for this category.
  • add some (named) categories. This is optional, but makes live easier if you want to modify logging behaviour for parts of your application, like sending it to other appenders or restricting your logging to certain levels.
  • add some impliciet categories. this is done by using the keys debug, info, warning, error, fatal and off to specify a maximum level for certain categories.

Your config will look a bit like this ( only with some real values…. ) :

//LogBox DSL
logBox = {
    // The configuration file without fileextension to use for operation, instead of using this structure
    configFile = "config/LogBox", 
    // Appenders
    appenders = {
        appenderName = {
            class="class.to.appender", 
            layout="class.to.layout",
            levelMin=0,
            levelMax=4,
            properties={
                name  = value,
                prop2 = value 2
            }
    },
    // Root Logger
    root = {levelMin="FATAL", levelMax="DEBUG", appenders="*"},
    // Granular Categories
    categories = {
        "coldbox.system" = { levelMin="FATAL", levelMax="INFO", appenders="*"},
        "model.security" = { levelMax="DEBUG", appenders="console"}
    }
    // Implicit categories
    debug  = ["coldbox.system.interceptors"],
    info   = ["model.class", "model2.class2"],
    warn   = ["model.class", "model2.class2"],
    error  = ["model.class", "model2.class2"],
    fatal  = ["model.class", "model2.class2"],
    off    = ["model.class", "model2.class2"]
};

Now you can start logging. In a model the most flexible way to do this is by wirebox injection, e.g

property name="log" inject="logbox:logger:{this}";
//or
property name="myLog" inject="logbox:logger:models.sub.myService";
//or
property name="perfLog" inject="logbox:logger:performance";

In a handler or interceptor it is even easier. You can do the same injections to create named loggers, but there is also a preconfigured logger called log so you can just start logging by calling

log.info("Hi there");
log.fatal("Terrible crash!");

So now you know how to start logging I will explain some concepts and some stuff which is confusing or unclear in the Logbox manual.

Continue reading
« Older posts

© 2024 ShiftInsert.nl

Theme by Anders NorenUp ↑