Js [].includes server side <> client side

#1

This code crashes server side, whereas it is OK client side:
c3typ is:

entity type Vert schema name 'VERT' {
    testLiteral: member function(origin: string): boolean js server
    staticTestLiteral: function(origin: string): boolean js server
}

javascript code is:

function testLiteral (origin) {
    return ['MIG','customer'].includes(origin);
}

function staticTestLiteral (origin) {
    return ['MIG','customer'].includes(origin);
}

Test from Chrome:

testVert = TestVert.make()
{versionEdits: Array(0)}
testVert.testLiteral('lundi')
all.js?env=browser&compat:19763 POST https://emr-stage2.c3iot.com/api/1/engie-emr/jmdTest1/TestVert?action=testLiteral 500 (Server Error)
C3.client.sendRequest @ all.js?env=browser&compat:19763
request @ all.js?env=browser&compat:885
call @ all.js?env=browser&compat:589
c3Call @ all.js?env=browser&compat:97
_call @ all.js?env=browser&compat:2613
(anonymous) @ VM11606:5
(anonymous) @ VM11605:1
all.js?env=browser&compat:592 Uncaught C3.client.ActionError {name: "ActionError", message: "wrapped org.mozilla.javascript.EcmaError: TypeErro…n object lundi,mardi. (TestVert_testLiteral.js#5)", error: C3.t…s.Obj, stack: "Error: wrapped org.mozilla.javascript.EcmaError: T…88:20), <anonymous>:5:22)↵    at <anonymous>:1:10"}
call @ all.js?env=browser&compat:592
c3Call @ all.js?env=browser&compat:97
_call @ all.js?env=browser&compat:2613
(anonymous) @ VM11606:5
(anonymous) @ VM11605:1

// error message is: “wrapped org.mozilla.javascript.EcmaError: TypeError: Cannot find function includes in object lundi,mardi. (TestVert_testLiteral.js#5)”

TestVert.staticTestLiteral('lundi')
all.js?env=browser&compat:19763 POST https://emr-stage2.c3iot.com/api/1/engie-emr/jmdTest1/TestVert?action=staticTestLiteral 500 (Server Error)
C3.client.sendRequest @ all.js?env=browser&compat:19763
request @ all.js?env=browser&compat:885
call @ all.js?env=browser&compat:589
c3Call @ all.js?env=browser&compat:97
_call @ all.js?env=browser&compat:2613
(anonymous) @ VM11613:5
(anonymous) @ VM11612:1
all.js?env=browser&compat:592 Uncaught C3.client.ActionError {name: "ActionError", message: "wrapped org.mozilla.javascript.EcmaError: TypeErro…ct lundi,mardi. (TestVert_staticTestLiteral.js#9)", error: C3.t…s.Obj, stack: "Error: wrapped org.mozilla.javascript.EcmaError: T…03:20), <anonymous>:5:15)↵    at <anonymous>:1:10"}
call @ all.js?env=browser&compat:592
c3Call @ all.js?env=browser&compat:97
_call @ all.js?env=browser&compat:2613
(anonymous) @ VM11613:5
(anonymous) @ VM11612:100:

// error message is : “wrapped org.mozilla.javascript.EcmaError: TypeError: Cannot find function includes in object lundi,mardi. (TestVert_staticTestLiteral.js#9)”`

In contrast, executing those function is chrome is ok:

function testLiteral (jour) {
    return ['lundi','mardi'].includes(jour);
}

testLiteral('lundi')
true

function staticTestLiteral (jour) {
    return ['lundi','mardi'].includes(jour);
}

staticTestLiteral('lundi')
true
#2

That is expected. Array.prototype.includes is a function that was introduced as part of the ECMAScript 2016 specification, which is not supported by Rhino (the server-side JavaScript runtime)

See this ECMAScript compatibility table for more information on specific features and their availability across runtimes.

#3

The C3 equivalents should work server side and client side. For example: User.arry() will be an array of User objects. This array will have the same APIs server or client side. In general we recommend using C3 objects so as to avoid inconsistencies like the one you identified.

#4

Thanks for those quick answers.

#5

Hi Riley,
Have you some documentation, tutorials and code examples for the use of C3.typesys?
I know /static/jsduck/index.html but there no example of code.

In EngieApps, it is mainly used by engie-emr but usually with AType.fetch(…) and AType.array(…), or in test cases, with code like:

pmsArr = PhysicalMeasurementSeries.array();
var pms = TestApi.createEntity(ctx, "PhysicalMeasurementSeries", {
  id: "SP_" + sp.id + "_" + origin + "_interval_" + infix + suffix,
  servicePoint: sp,
  origin: origin,
  unitConstraint: unit,
  measurementType: measurementType,
  flowDirection: flowDirection,
  touCode: touCode,
  treatment: "integral"
});
pmsArr.push(pms);
#6

Not really… the example you posted is pretty good… any specific questions?

#7

My experience with C3.typesys is quite frustrating, because I’ve the filling that it is better to type the code but when I do that, I lost a lot of time to finishing with a not-typed code that works.

Last days, I lost a lot of time on “make” function.
For example, I added this method to ServicePoint

emrMake: function(id: string, emrRegisters: [EmrRegister], locationId: string, resourceId: string, slpId: string): ServicePoint js server

function emrMake (id, emrRegisters, facilityId, resourceId, slpId) {
    var sp = {
        facility: { id: facilityId },
        resource: { id: resourceId },
        slpSet: [{ to: { id: slpId } }],
        registerMeasurements: [],
        measurements: [],
    };
    var uom = EmrUom.getUom(resourceId);
    if (id) sp.id = id;
    _.each(emrRegisters, function (register) {
        _.each(register.series, function (indexSeries) {
            if (EmrOrigin.hasIndexSeries(indexSeries.origin)) {
                var rmsId = ['rm', sp.id, resourceId, register.touCode, register.measurementType, indexSeries.origin].join('-');
                var rmsData = indexSeries.getC3RegisterMeasurements(sp.id);
                var rms = RegisterMeasurementSeries.emrMake(
                    rmsId, 
                    sp.id, 
                    indexSeries.origin, 
                    register.measurementType, 
                    register.touCode, 
                    uom, 
                    rmsData
                );
                sp.registerMeasurements.push(rms);
            }
            if (EmrOrigin.hasConsumptionSeries(indexSeries.origin)) {
                var pmsData = indexSeries.getC3Measurements(sp.id);
                var pmsId = ['pm', sp.id, resourceId, register.touCode, register.measurementType, indexSeries.origin].join('-');
                var pms = PhysicalMeasurementSeries.emrMake(
                    pmsId, 
                    sp.id, 
                    indexSeries.origin, 
                    register.measurementType, 
                    register.touCode, 
                    uom, 
                    pmsData
                );
                sp.measurements.push(pms);
            }
        });
    });
    return ServicePoint.make(sp);
}

but it fails because (I think) ServicePoint.make server side return the a “shallow” ServicePoint (no registerMeasurements, measurements).

This version works:

emrMakeTest: function(id: string, emrRegisters: [EmrRegister], locationId: string, resourceId: string, slpId: string): json js server

with same code except last line:

function emrMakeTest (id, emrRegisters, facilityId, resourceId, slpId) {
   ...
    return sp;
}
#8

Its helpful to differentiate between Javascript objects and instances of c3 types. For example this code:

sp = {
...
}

does not create a ServicePoint but instead a javascript object that might or might not be a valid ServicePoint.

The c3 equivalent is

sp = ServicePoint.make({

}) 

But this command just creates a ServicePoint locally, it doesn’t persist anything in the db. For that you want:
sp = ServicePoint.create({})

Finally, note that individual c3 types must be inserted to the DB individually. Lets say you want to create a service point with an associated PMS.

var sp = ServicePoint.create({id: 'youChoose'})
var pms = PhysicalMeasurementSeries.create({id: 'whatever', servicePoint: sp});

You need to differentiate between

  1. direct references (e.g. the ‘servicePoint’ field on PhysicalMeasurementSeries)
  2. foreign key references (e.g. the inverse ‘measurements’ field on ServicePoint)

Finally, a few points of advice:

It is almost always a mistake to use ‘json’ as a return type or input type to a function. No one who uses your code will ever know what keys/values to put in that json. Instead, use an existing type or define a new type

Work with instances of c3 types, not with javascript objects. This will keep your code consistent whether you make an object in the local scope or fetch it from a database or are passed it from another function. This has the added benefit of making python and javascript code much more similar and understandable to devs of the other language. As system complexity increases this becomes even more important

#9

This could help you with some good hints?

function emrMake (id, emrRegisters, facilityId, resourceId, slpId) {
    var sp = ServicePoint.make({
        facility: facilityId , // small hint, you can just use ids as references
        resource: resourceId 
        //slpSet: [{ to: { id: slpId } }], <-sorry, i don't know what this is...
    }),  measurements: Measurement.array();
    var uom = EmrUom.getUom(resourceId);

    // c3 instances are immutable, use 'putField' to return a new instance
    if (id) sp = sp.putField("id", id);
    _.each(emrRegisters, function (register) {
        _.each(register.series, function (indexSeries) {
          
            if (EmrOrigin.hasConsumptionSeries(indexSeries.origin)) {
                var pmsData = indexSeries.getC3Measurements(sp.id);
                var pmsId = ['pm', sp.id, resourceId, register.touCode, register.measurementType, indexSeries.origin].join('-');      
                var pms = PhysicalMeasurementSeries.emrMake( // I'm not 100% sure what this function does since you guys wrote it, but as long as the 'sp.id' gets put in the servicePoint field of the measurement series it should be good!
                    pmsId, 
                    sp.id, 
                    indexSeries.origin, 
                    register.measurementType, 
                    register.touCode, 
                    uom, 
                    pmsData
                );
            }
        });
    });
    return ServicePoint.create(sp);
}