Asset Creation through Transaction in Hyperledger Composer - hyperledger

While creating any asset or participant need to check some condition Like (IF..THEN..ELSE) on some field.
Is it Possible to create Asset or Participant through transaction?

Yes it is possible.
I did the same thing in my network, creating assets with a transaction and applying whatever rules you need.
transactions are run from your logic.js file in lib.
assume you have an asset myAsset in org.myAssets namespace
asset myAsset identified by assetId
{
o String assetId
o String someData
//other fields as required
}
you now want a transaction which will create an asset
your cto transaction looks like this:
namespace org.transactions
import org.myAssets.*
transaction MyAssetCreate
{
o myAsset anAsset
}
you can't have a reference to the asset here since you won't have an asset yet.
in your lib/logic.js you have something like:
/**
* creates an asset
* #param {org.transactions.MyAssetCreate} myAssetCreate
* #transaction
*/
async function MyAssetCreate(myAssetCreate) {
return getAssetRegistry('org.myAssets.myAsset')
.then(function(result) {
var factory = getFactory();
var newAsset = factory.newResource(
'org.myAssets',
'myAsset',
myAssetCreate.anAsset.assetId);
newAsset.someData = myAssetCreate.anAsset.someData
//continue with property assignments and any logic you have
//when you are done and everything looks good you can continue
return result.add(newAsset);
});
now you can invoke MyAssetCreate and you will get your asset in the right registry.
Of course, if you do this then you need to make sure you don't allow assets to be created via the standard asset endpoint.
I myself plan to not expose any asset endpoints at all and only allow changes via transactions.
Check the code for typos etc, I took this from my running network, replacing my type names so it's possible I mistyped something.

Related

Using global shared libraries in Jenkins to define parameter options

I am trying to use a global class that I've defined in a shared library to help organise job parameters. It's not working, and I'm not even sure if it is possible.
My job looks something like this:
pipelineJob('My-Job') {
definition {
// Job definition goes here
}
parameters {
choiceParam('awsAccount', awsAccount.ALL)
}
}
In a file in /vars/awsAccount.groovy I have the following code:
class awsAccount implements Serializable {
final String SANDPIT = "sandpit",
final String DEV = "dev",
final String PROD = "prod"
static String[] ALL = [SANDPIT, DEV, PROD]
}
Global pipeline libraries are configured to load implicitly from the my repository's master branch.
When attempting to update the DSL scripts I receive the error:
ERROR: (myJob.groovy, line 67) No such property: awsAccount for class: javaposse.jobdsl.dsl.helpers.BuildParametersContext
Why does it not find the class, and is it even possible to use shared library classes like this in pipeline job?
Disclaimer: I know it works using Jenkinsfile. Unfortunatelly, not tested usng Declarative Pipelines - but no answers yet, so it may be worth a try
Regarding your first question: there are some reasons why a class from your shared-lib could not be found. Starting from the library import, the library syntax, etc. But they definitvely work for DSL. To be more precise about it, additional information would be great. But be sure that:
You have your groovy class definition using exactly the directory structure as described in the documentation (https://www.jenkins.io/doc/book/pipeline/shared-libraries/)
Give a name to the shared-lib in jenkins as you configure it and be sure is exactly the name you use in the import
Use the import as described in the documentation (under Using Libraries)
Regarding your second question (the one that names this SO question): yes, you can include parameter jobs from information in your shared-lib. At least, using Jenkinsfiles. You can even define properties to be included in the pipelie. I got it working with a tricky syntax due to different problems.
Again, I am using Jenkinsfile and this is what worked for me:
In my shared-lib class, I added a static function that introduces the build parameters. Notice the input parameters that function needs and its usage:
class awsAccount implements Serializable {
//
static giveMeParameters (script) {
return [
// Some parms
script.string(defaultValue: '', description: 'A default parameter', name: 'textParm'),
script.booleanParam(defaultValue: false, description: 'If set to True, do whatever you need - otherwise, do not do it', name: 'boolOption'),
]
}
}
To introduce those parameters in the pipeline, you need to place the returned value of the function into the parameters array
properties (
parameters (
awsAccount.giveMeParameters (this)
)
Again, notice the syntax when calling the function. Similar to this, you can also define functions in the shared-lib that return properties and use them in multiple jobs (disableConcurrentBuilds, buildDiscarder, etc)

Groovy read file line by line and store it into list

I have a file called apps.txt which has three app names in it
frontendapp
authorizationservice
connectorservice*
In jenkins pipeline i want to perform some operation on them one by one so i am trying to get them in groovy list using this code -
list = readFile.readFileLineByLine("${workspace}/apps.txt").collect {it}
for (item in list) {
println "I need to perform some operations on files"
}
But getting groovy.lang.MissingPropertyException.
If i use file class like this - list = new File("${workspace}/apps.txt").collect {it} then it search for a file on Jenkins master node only and i get fileNotFoundException.
If i use list = readFile("${workspace}/apps.txt").collect {it} then list gets values character by character. How i can get app names from apps.txt inorder to perform operation on each app.
Your attempts are close, but mix things up.
Those are working ways:
def list = new File("${workspace}/apps.txt").text.readLines()
Note the .text call inbetween.
def list = readFile("${workspace}/apps.txt").readLines()
Or with the helper Jenkins provides.
Side note: .collect{it} is just .collect() - and usually is only
needed to copy a list. Since the read lines from the file are already
eagerly read, those are copies.
This work for me in a windows worker
script {
def list = readFile("filesChanged").readLines()
for(item in list){
print item
bat('type '+item)
}
}

How to implement Spock Parameterized test best practice?

I have a test spec, which can be run with a unique data set. The best practice for this is a bit unclear. How should the code below be modified to run with:
#Stepwise
class marktest extends ShopBootStrap {
private boolean useProductionUrl = false
def "Can Access Shop DevLogin page"() {
// DevStartLogin: 'New OE Start' button click
setup:
println System.getProperty("webdriver.chrome.driver")
when:
to ShopDevStartPage
then:
at ShopDevStartPage
}
def "on start enrollment, select 'United States' and click 'continue' button"() {
when: "enter Sponsor ID and click New OE Start"
to ShopDevStartPage
sponsorId.value(ShopDevStartPage.SPONSORID)
NewOEButton.click()
then:
waitFor { NewEnrollmentPage }
}
}
1) data set 1
private boolean useProductionUrl = false
protocol = System.getProperty("protocol") ?: "https"
baseDomain = System.getProperty("base.url") ?: "beta.com"
testPassword = System.getProperty("test.password") ?: "dontyouwish"
2) data set 2
private boolean useProductionUrl = true
protocol = System.getProperty("protocol") ?: "https"
baseDomain = System.getProperty("base.url") ?: "production.com"
testPassword = System.getProperty("test.password") ?: "dywyk"
Generally, to make a test depend on data, use a where block, possibly together with the #Unroll annotation.
However, your case is simply not the best example for a data driven test.
The baseDomain and protocol should rather be set in the GebConfig.groovy, similar to the snippets you provided.
Refer to this section in the Book of Geb, as that is what you are using.
Simple example (in GebConfig.groovy):
environments {
production {
baseUrl = "https://production.com"
}
beta {
baseUrl = "https://beta.com"
}
}
If done this way, your individual tests does not need to care about the environment, as this is already built into Geb.
For example when navigating to pages, their base URL is automatically set.
You did not provide that part of the code in your example (how the pages are defined), so I cannot help you with that directly.
Now, in your case, as far as the "password" is concerned, you could read that from an environment variable or system property, that you set close to where you configure Geb with geb.env or geb.build.baseUrl system properties.
Note I am just considering this for practial reasons without any regards towards secrecy of the password.
You would pick up the variable in the page class that uses it.
Example code in page class:
static content = {
//...
passwordInput = { $('input[type="password"]') }
//...
}
void enterPassword() {
passwordInput.value(System.getProperty('test.password'))
}
To make this work, you need to start your test with the system properties set to correct values.
E.g. if starting directly from the command line, you would add parameters -Dgeb.env=beta -Dtest.password=dontyouwish.
If running from a Gradle task, you would need to add appropriate systemProperty keys and values to that task.
If running from IDE, refer to your IDEs documentation on how to set Java system properties when running a program.

Hyperleger Composer "namespace already exists" error on "composer archive create" command

when execute command:
composer archive create --sourceType dir --sourceName /home/testuser/test-network -a /home/testuser/test-network/dist/test-network.bna
I get an error:
Creating Business Network Archive
Looking for package.json of Business Network Definition
Input directory: /home/testuser/test-network
/usr/lib/node_modules/composer-cli/node_modules/yargs/yargs.js:1079
else throw err
^
Error: namespace already exists
at ModelManager.addModelFiles (/usr/lib/node_modules/composer-cli/node_modules/composer-common/lib/modelmanager.js:234:31)
at Function.fromDirectory (/usr/lib/node_modules/composer-cli/node_modules/composer-common/lib/businessnetworkdefinition.js:493:43)
at Function.handler (/usr/lib/node_modules/composer-cli/lib/cmds/archive/lib/create.js:80:42)
at Object.module.exports.handler (/usr/lib/node_modules/composer-cli/lib/cmds/archive/createCommand.js:31:30)
at Object.self.runCommand (/usr/lib/node_modules/composer-cli/node_modules/yargs/lib/command.js:233:22)
at Object.Yargs.self._parseArgs (/usr/lib/node_modules/composer-cli/node_modules/yargs/yargs.js:990:30)
at Object.self.runCommand (/usr/lib/node_modules/composer-cli/node_modules/yargs/lib/command.js:204:45)
at Object.Yargs.self._parseArgs (/usr/lib/node_modules/composer-cli/node_modules/yargs/yargs.js:990:30)
at Object.get [as argv] (/usr/lib/node_modules/composer-cli/node_modules/yargs/yargs.js:927:19)
at Object.<anonymous> (/usr/lib/node_modules/composer-cli/cli.js:58:5)
I have changed the files to build the network and even I get the error with the example files:
File /home/testuser/test-network/lib/logic.js:
function sampleTransaction(tx) {
// Save the old value of the asset.
var oldValue = tx.asset.value;
// Update the asset with the new value.
tx.asset.value = tx.newValue;
// Get the asset registry for the asset.
return getAssetRegistry('org2.acme.sample2.SampleAsset')
.then(function (assetRegistry) {
// Update the asset in the asset registry.
return assetRegistry.update(tx.asset);
})
.then(function () {
// Emit an event for the modified asset.
var event = getFactory().newEvent('org2.acme.sample2', 'SampleEvent');
event.asset = tx.asset;
event.oldValue = oldValue;
event.newValue = tx.newValue;
emit(event);
});
}
File /home/testuser/test-network/test.cto:
namespace org2.acme.sample2
asset SampleAsset identified by assetId {
o String assetId
--> SampleParticipant owner
o String value
}
participant SampleParticipant identified by participantId {
o String participantId
o String firstName
o String lastName
}
transaction SampleTransaction {
--> SampleAsset asset
o String newValue
}
event SampleEvent {
--> SampleAsset asset
o String oldValue
o String newValue
}
I have tried to change the namespace too and I got the same error
ok, its because you have multiple .cto files (ie in your directory) with the same namespace contained in them (perhaps you are making different editions or wanting multiple .cto files). The archive command checks for namespaces in each CTO). Each busness network model has a single namespace. All resource declarations within the file are implicitly in this namespace. You can have multiple .cto files if you want to break it out - but don't repeat the namespace in the additional files. You can even, if you want, have multiple model files, with different namespaces (if that's what you want of course).
See https://hyperledger.github.io/composer/reference/cto_language.html
Otherwise, suggest to move any 'editions' of the CTO files with the same namespace name.
Then try build your .bna file again.

Copying default external configuration on first run of Grails web app

In our Grails web applications, we'd like to use external configuration files so that we can change the configuration without releasing a new version. We'd also like these files to be outside of the application directory so that they stay unchanged during continuous integration.
The last thing we need to do is to make sure the external configuration files exist. If they don't, then we'd like to create them, fill them with predefined content (production environment defaults) and then use them as if they existed before. This allows any administrator to change settings of the application without detailed knowledge of the options actually available.
For this purpose, there's a couple of files within web-app/WEB-INF/conf ready to be copied to the external configuration location upon the first run of the application.
So far so good. But we need to do this before the application is initialized so that production-related modifications to data sources definitions are taken into account.
I can do the copy-and-load operation inside the Config.groovy file, but I don't know the absolute location of the WEB-INF/conf directory at the moment.
How can I get the location during this early phase of initialization? Is there any other solution to the problem?
There is a best practice for this.
In general, never write to the folder where the application is deployed. You have no control over it. The next rollout will remove everything you wrote there.
Instead, leverage the builtin configuration capabilities the real pro's use (Spring and/or JPA).
JNDI is the norm for looking up resources like databases, files and URL's.
Operations will have to configure JNDI, but they appreciate the attention.
They also need an initial set of configuration files, and be prepared to make changes at times as required by the development team.
As always, all configuration files should be in your source code repo.
I finally managed to solve this myself by using the Java's ability to locate resources placed on the classpath.
I took the .groovy files later to be copied outside, placed them into the grails-app/conf directory (which is on the classpath) and appended a suffix to their name so that they wouldn't get compiled upon packaging the application. So now I have *Config.groovy files containing configuration defaults (for all environments) and *Config.groovy.production files containing defaults for production environment (overriding the precompiled defaults).
Now - Config.groovy starts like this:
grails.config.defaults.locations = [ EmailConfig, AccessConfig, LogConfig, SecurityConfig ]
environments {
production {
grails.config.locations = ConfigUtils.getExternalConfigFiles(
'.production',
"${userHome}${File.separator}.config${File.separator}${appName}",
'AccessConfig.groovy',
'Config.groovy',
'DataSource.groovy',
'EmailConfig.groovy',
'LogConfig.groovy',
'SecurityConfig.groovy'
)
}
}
Then the ConfigUtils class:
public class ConfigUtils {
// Log4j may not be initialized yet
private static final Logger LOG = Logger.getGlobal()
public static def getExternalConfigFiles(final String defaultSuffix, final String externalConfigFilesLocation, final String... externalConfigFiles) {
final def externalConfigFilesDir = new File(externalConfigFilesLocation)
LOG.info "Loading configuration from ${externalConfigFilesDir}"
if (!externalConfigFilesDir.exists()) {
LOG.warning "${externalConfigFilesDir} not found. Creating..."
try {
externalConfigFilesDir.mkdirs()
} catch (e) {
LOG.severe "Failed to create external configuration storage. Default configuration will be used."
e.printStackTrace()
return []
}
}
final def cl = ConfigUtils.class.getClassLoader()
def result = []
externalConfigFiles.each {
final def file = new File(externalConfigFilesDir, it)
if (file.exists()) {
result << file.toURI().toURL()
return
}
final def error = false
final def defaultFileURL = cl.getResource(it + defaultSuffix)
final def defaultFile
if (defaultFileURL) {
defaultFile = new File(defaultFileURL.toURI())
error = !defaultFile.exists();
} else {
error = true
}
if (error) {
LOG.severe "Neither of ${file} or ${defaultFile} exists. Skipping..."
return
}
LOG.warning "${file} does not exist. Copying ${defaultFile} -> ${file}..."
try {
FileUtils.copyFile(defaultFile, file)
} catch (e) {
LOG.severe "Couldn't copy ${defaultFile} -> ${file}. Skipping..."
e.printStackTrace()
return
}
result << file.toURI().toURL()
}
return result
}
}

Resources