Fabric Composer test code not working - blockchain

I´ve just replaced the Composer default sample ("sampleAsset", "sampleTransaction", etc) by another one I created, for my better understanding. Everything works except for the transaction, which return me the error message:
"**Error: Could not find any functions to execute for transaction org.acme.sample.CompraDoVinho#**2b2d0624-bc..."
Find below the source codes:
Blockquote
Model file:
namespace org.acme.sample
asset Vinho identified by IDvinho {
o String IDvinho
--> Participante owner
o String uva
o String nomeVinho
o Integer preco
}
participant Participante identified by IDparticipante {
o String IDparticipante
o String tipo
o String nomeEmpresa
}
transaction CompraDoVinho identified by IDcompra {
o String IDcompra
--> Vinho asset
o Integer precoVenda
}
Logic:
function onSampleTransaction(CompraDoVinho) {
CompraDoVinho.asset.preco = CompraDoVinho.precoVenda;
return getAssetRegistry('org.acme.sample.Vinho')
.then(function (assetRegistry) {
return assetRegistry.update(CompraDoVinho.asset);
});
}
Permissions:
rule Default {
description: "Allow all participants access to all resources"
participant: "ANY"
operation: ALL
resource: "org.acme.sample"
action: ALLOW
}
Blockquote
Could anybody help me finding where is the bug in my code?
Thanks in advance

The issue is almost certainly because you've renamed the transaction. Composer has 2 mechanisms to route transactions to JS functions:
(Legacy) using an onMyTransactionType naming convention. I.e. the function will be called when an instance of MyTransactionType is submitted.
(Preferred) using the #transaction and #param annotations. See below for an example. The #transaction annotation indicates that the function would like to process transactions and the #param annotation is used to specify the type of the transaction to process.
/**
* Place an order for a vehicle
* #param {org.acme.vehicle.lifecycle.manufacturer.PlaceOrder} placeOrder - the PlaceOrder transaction
* #transaction
*/
function placeOrder(placeOrder) {
console.log('placeOrder');
let factory = getFactory();
let NS = 'org.acme.vehicle.lifecycle.manufacturer';
let order = factory.newResource(NS, 'Order', placeOrder.transactionId);
order.vehicleDetails = placeOrder.vehicleDetails;
order.orderStatus = 'PLACED';
order.manufacturer = placeOrder.manufacturer;
// save the order
return getAssetRegistry(order.getFullyQualifiedType())
.then(function (registry) {
return registry.add(order);
});
}

Absolutely. The annotation is essential for the function to work!
#param must state the class name of the transaction and the param name
#transaction declared underneath, with function to follow in block below
#param {org.acme.mynetwork.Foo} foo - the report to be processed
* #transaction

Please Replace the code in your logic.js file with following code and the error will surely be gone. Mine was the same problem, I just added the required JS doc annotations above the function and the same issue was resolved!
'use strict';
var NS = 'org.acme.sample';
/**
* #param {org.acme.sample} CompraDoVinho
* #transaction
*/
function onSampleTransaction(CompraDoVinho) {
CompraDoVinho.asset.preco = CompraDoVinho.precoVenda;
return getAssetRegistry('org.acme.sample.Vinho')
.then(function (assetRegistry) {
return assetRegistry.update(CompraDoVinho.asset);
});
}
Hope this helps you!

Related

Google Workspace: Add all users in a child organization unit to a group using Google Apps Script

I'd like to add all users in a child Organization Unit to a group. I can do that in Admin Dashboard but it only shows 50 users at a time. Since we have thousands of users in each child OU, this process would be inconvenience.
My solution:
I followed a guide (Source) and used Google Apps Script to run the following code but it simply didn't do anything. The log shows "Execution started" then "Execution completed" but no user is moved to the group. I suspect the format for the OU in the code is wrong. It is a bit tricky to get it right especially the OU is Arabic (Right to Left). Any idea what could be wrong?
function myFunction() {
/**
* Add all users of an organizational unit (OU) to specific groups
* in Google Workspace
*
* Usage:
* Change the OU variable, in a format of /OU/SubOU/SubSubOU. The root OU is represented as /
* Change the groupEmails variable, which is a list of group emails.
*
* © 2021 xFanatical, Inc.
* #license MIT
*
* #since 1.0.0 proof of concept
*/
const OU = '/كلية الطب/الطلبة/طلبة الدراسات الاولية 2019 - 2020/testing'
const groupEmails = ['100gb.limit#uokufa.edu.iq']
function addAllOUUsersToGroup() {
let pageToken
let page
do {
page = AdminDirectory.Users.list({
customer: 'my_customer',
maxResults: 100,
pageToken,
query: `orgUnitPath='${OU}'`,
})
let users = page.users
if (users) {
users.forEach((user) => {
groupEmails.forEach((groupEmail) => {
try {
AdminDirectory.Members.insert({
email: user.primaryEmail,
role: 'MEMBER',
type: 'USER',
}, groupEmail)
Logger.log(`Added user [${user.primaryEmail}] to group [${groupEmail}]`)
} catch (e) {
Logger.log(`Failed to add user [${user.primaryEmail}] to group [${groupEmail}], error: ${e.details && e.details.message && e.details.message}`)
}
})
})
} else {
Logger.log('No users found.')
}
pageToken = page.nextPageToken
} while (pageToken)
}
}
Issue:
You are not executing the function addAllOUUsersToGroup.
addAllOUUsersToGroup is declared inside myFunction, but it is never called. Therefore, if you execute myFunction, addAllOUUsersToGroup won't run.
Solution:
Either call addAllOUUsersToGroup inside myFunction. For example:
function myFunction() {
// ...stuff...
function addAllOUUsersToGroup() {
// ...stuff...
}
addAllOUUsersToGroup(); // <== ADD THIS
}
Or, alternatively, take the function addAllOUUsersToGroup outside myFunction and call it directly:
function addAllOUUsersToGroup() { <== THIS IS NOT INSIDE myFunction
// ...stuff...
}
Reference:
Calling functions

Doctrine ORM produce different output when using manual 'select'

I'm a beginner with Doctrine ORM (v2.5.5) and Silex (v2.0.4)/Symfony (v3.1.6). I need to output my Date field to the YYYY-MM-DD format. Let's say I have this annotation and getter method on my Entity:
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
// getter
public function getExpire()
{
return !is_object($this->expire) ? new \DateTime() : $this->expire->format('Y-m-d');
}
Here's my simplified controller for debugging purpose:
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$normalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer();
$encoder = new \Symfony\Component\Serializer\Encoder\JsonEncoder();
$normalizer->setCircularReferenceHandler(function($obj){
return $obj->getId();
});
$serializer = new \Symfony\Component\Serializer\Serializer(array($normalizer), array($encoder));
$qb = $tnkbRepo->createQueryBuilder('c')
->setMaxResults(1);
//$query = $qb->getQuery(); // [1] <<-- this line produce proper YYYY-MM-DD format
//$query = $qb->select('c.expire')->getQuery(); // [2] <<-- this (manual select) line produce DateTime object.
$results = $query->getResult();
return $serializer->serialize($results, 'json');
});
With the first [1] line uncommented I got the proper output I wanted:
[more json output here]...,"expire":"1970-10-25",...
But with the second [2] line uncommented (I intendedly omitted other fields for testing) I got the following output, which wasn't what I expected:
[{"expire":{"timezone":{"name":"UTC","location":{"country_code":"??","latitude":0,"longitude":0,"comments":""}},"offset":0,"timestamp":25660800}}]
I also noticed, with the [2] line Doctrine seems to ignore my entity's getter method (I tried returning empty string). I expect the output will be the same as the [1] case, it makes me curious. My questions are:
How do I achieve the same proper YYYY-MM-DD format with the [2] version?
And why are they produce different output format?
Thank you.
UPDATE
More simplified /debug controller for testing (no serialization):
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$qb = $tnkbRepo->createQueryBuilder('c');
// [1a] normal query. doesn't return Entity, getExpire() isn't called.
/*$query = $qb->select('c.expire')
->setMaxResults(1)->getQuery();*/
// [2a] partial query. returns Entity, getExpire() called.
/*$query = $qb->select('partial c.{id,expire}')
->setMaxResults(1)->getQuery();*/
$results = $query->getResult();
var_dump($results);die;
});
Updated Entity method getExpire():
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
protected $dateAsString = true;
protected $dateFormat = 'Y-m-d';
// getter
public function getExpire()
{
return ($this->expire instanceof \DateTime) ? $this->dateOutput($this->expire)
: $this->dateOutput(new \DateTime());
}
protected function dateOutput(\DateTime $date) {
if ($this->dateAsString) {
return $date->format($this->dateFormat);
}
return $date;
}
Controller dump results:
[1a] normal query:
// non-entity
array(1) { [0]=> array(1) { ["expire"]=> object(DateTime)#354 (3) { ["date"]=> string(26) "1970-10-25 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } }
[2a] partial object query:
// array of entity
array(1) { [0]=> object(App\Entity\Tnkb)#353 (23) { /* more properties */...["expire":protected]=> object(DateTime).../* more properties */
I found out this is normal behaviour with Doctrine, it has something to do with Partial Objects. See my comment below. Link: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html
I don't think it's good practice to return a \DateTime sometimes but a formatted string other times. However maybe you have your reasons for doing this.
The only reason I can think of the difference in results is if Doctrine calls getters on the properties when loading an entity. I tested with a simple class which has the same expire property and getter. Returning the class still had the serialized (not formatted) \DateTime object, which would suggest that at some point your getter is being called and the property set to a new \DateTime.
My recommendation is to look at the DateTimeNormalizer provided by Symfony in 3.1. If you can't upgrade to 3.1 then you can easily build your own one. Then you can be sure you'll always have consistent \DateTime format in all your responses. You can all remove the ->format(...) from your getter then and always return a \DateTime object. I think this is a much cleaner approach.

Large number of queries if templates stored in database, or error on delete from cache

With storing a few templates in a database as found in this answer there is a great increase in the number of database queries. For example, profiler shows 12 of 20 queries on a page were related to searching for templates although none was stored in the database. I was under the impression that searches were done only if a template was not available within the file structure. Is there a method to query the database only if the required template is not in the file structure?
Update 1:
Experiments have shown that commenting out the tags section of the vol.volbundle.twig_database_loader service eliminates the unnecessary queries but can still find a template in the database. Without the tags block the following error occurs
InvalidArgumentException: Template name "new_opp" is not valid (format
is "bundle:section:template.format.engine"
when a template is edited, persisted and an attempt is made to delete its predecessor from cache with the following code. [With the tags block present, the cached template is deleted!] This appears to be the case because the tags block allows the database loader to be found during cache delete.
$fileCache = $this->container->get('twig')->getCacheFilename($name);
if (is_file($fileCache)) {
#unlink($fileCache);
}
So it is odd, then, that the database loader is used by the controller when the block is not present.
services.yml
vol.volbundle.twig_database_loader:
class: Vol\VolBundle\Tools\TwigDatabaseLoader
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: twig.loader }
TwigDatabaseLoader
namespace Vol\VolBundle\Tools;
use \Twig_Error_Loader;
/**
* Description of DatabaseTwigLoader
*
*/
class TwigDatabaseLoader implements \Twig_LoaderInterface
{
private $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function getSource($name)
{
if (false === $source = $this->getValue('source', $name)) {
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
}
return $source;
}
public function isFresh($name, $time)
{
if (false === $lastModified = $this->getValue('last_modified', $name)) {
return false;
}
return $lastModified <= $time;
}
public function getCacheKey($name)
{
// check if exists
return 'db:' . $name;
}
protected function getValue($column, $name)
{
$conn = $this->entityManager->getConnection();
$sth = $conn->prepare('SELECT '.$column.' FROM template WHERE name = :name');
$sth->execute(array(':name' => (string) $name));
return $sth->fetchColumn();
}
}
Compiler
namespace Vol\VolBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Description of TwigDatabaseLoaderPass
*
*/
class TwigDatabaseLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('twig');
$definition->addMethodCall('setLoader', array(new Reference('vol.volbundle.twig_database_loader')));
}
}
It turns out that the large number of queries only occurs in dev mode! This was determined by enabling the query log in MySQL. Exercising the editing of a template and then rendering it then reviewing the log showed that only the stored template invoked a template query. None of the other templates appeared in the query log. Case closed!

How to get associations in Doctrine entity?

I hope it's possible in Doctrine2. I know Propel does that automatically. What I want to do is this:
I have two tables:
workflow (id, name)
inbox (id, workflow_id, name)
And two entities:
Workflow and Inbox
In my Inbox entity I, of course, have this (to relate two tables):
/**
* #ORM\ManyToOne(targetEntity="Workflow")
* #ORM\JoinColumn(nullable=false)
*/
protected $workflow;
Everything works great. However, I want to be able to get inboxes from the Workflow entity that are associated with that workflow. I can't find how to do it.
Propel does that very simple, you would just do something like this:
$workflow = WorkflowQuery::create()
->filterById(1)
->findOne(1);
$inboxes = $workflow->getInboxs()
//Propel just addes 's' to methods that return associations
How, similar this can be done in Doctrine2? Something like this:
$workflow = $this->getRepository('MyBundle:Workflow')->findById(1);
$inboxes = $workflow->getInboxes();
So, is there a way to do this? Thank you.
Change in controller:
$workflow = $this->getDoctrine()->getRepository('MyBundle:Workflow')->find(1);
$inboxes = $workflow->getInboxes();
Don't forget that you need
// Workflow entity
public function __construct()
{
// make `use` statement for this, not long
$this->inboxes = new \Doctrine\Common\Collections\ArrayCollection() ;
}
and
/**
* #ORM\OneToMany(targetEntity="Inbox", mappedBy="workflow", cascade={"persist"})
*/
protected $inboxes ;
public function getInboxes() { return $this->inboxes ; }
// setInboxes(), addInbox(), removeInbox() here

Cannot save a Doctrine_Collection

I am using Docrine 1.2 with Zend Framework and trying to save a Doctrine Collection.
I am retrieving my collection from my table class with the following code.
public function getAll()
{
return $this->createQuery('e')
->orderBy('e.order ASC, e.eventType ASC')
->execute();
}
I also have the following class to reorder the above event records.
class Admin_Model_Event_Sort extends Model_Abstract
{
/**
* Events collection
* #var Doctrine_Collection
*/
protected $_collection = null;
public function __construct()
{
$this->_collection = Model_Doctrine_EventTypesTable::getInstance()->getAll();
}
public function save($eventIds)
{
if ($this->_collection instanceof Doctrine_Collection) {
foreach ($this->_collection as $record)
{
$key = array_search($record->eventTypeId, $eventIds);
if ($key !== false) {
$record->order = (string)$key;
}
}
return $this->_saveCollection($this->_collection);
} else {
return false;
}
}
}
The _saveCollection method above is as follows
/**
* Attempts to save a Doctrine Collection
* Sets the error message property on error
* #param Doctrine_Collection $collection
* #return boolean
*/
protected function _saveCollection(Doctrine_Collection $collection)
{
try {
$collection->save();
return true;
} catch (Exception $e) {
$this->_errorMessage = $e->getMessage();
OpenMeetings_Logger_ErrorLogger::write('Unable to save Doctrine Collection');
OpenMeetings_Logger_ErrorLogger::vardump($this->_errorMessage);
return false;
}
}
The event id's in the above save method is simply an enumerated array of event id's, I am using the keys of the array to set the sort order of the events using the order field. If I do a var_dump of the collection to an array ($this->_collection->toArray()) I get the correct data. However when I attempt to save the collection I get the following error.
"SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order = '0' WHERE eventtypeid = '3'' at line 1"
Is there anyway I can get Doctrine to expand on this error, the full SQL statement would be a start, also if anyone knows as to why this error is occuring then that would be very helpful.
Many thanks in advance
Garry
EDIT
I have modified my above code to try to work one record at a time but I still get the same problem.
public function save($eventIds)
{
foreach ($eventIds as $key => $eventId) {
$event = Model_Doctrine_EventTypesTable::getInstance()->getOne($eventId);
$event->order = (string)$key;
$event->save();
}
}
Ok I have found the problem. I was using the MYSQL reserved word order as a field name thus the error, changed it to sortOrder and the problem went away.
Hope this helps someone with a similar issue.
Garry