Related
Great Expectations provides the ability to produce Html reports using DataDocs as shown in the folloiwng example:
I would like the change the following defaults in the header - see image
The report is generated using the following
validation_results_stg = ge_stg_update.validate(expectation_suite='stg_expectations.html', only_return_failures=False)
I believe I can make changes because if I run the code validatation_results_stg
I get the following output
{
"evaluation_parameters": {},
"meta": {
"great_expectations_version": "0.13.38",
"expectation_suite_name": "default",
"run_id": {
"run_time": "2021-11-18T14:59:49.831733+00:00",
"run_name": null
},
"batch_kwargs": {
"ge_batch_id": "2c157d12-4880-11ec-8b5e-000d3ad66fea"
},
"batch_markers": {},
"batch_parameters": {},
"validation_time": "20211118T145949.831530Z",
"expectation_suite_meta": {
"great_expectations_version": "0.13.38"
}
},
"results": [
{
"result": {
"element_count": 14539,
"missing_count": 0,
"missing_percent": 0.0,
"unexpected_count": 0,
"unexpected_percent": 0.0,
"unexpected_percent_total": 0.0,
"unexpected_percent_nonmissing": 0.0,
"partial_unexpected_list": []
},
"exception_info": {
"raised_exception": false,
"exception_message": null,
"exception_traceback": null
},
"meta": {},
"success": true,
"expectation_config": {
"kwargs": {
"column": "SERVICE",
"value_set": [
"CMC",
"Divorce",
"Probate",
"SSCS"
]
},
"expectation_type": "expect_column_values_to_be_in_set",
"meta": {},
"ge_cloud_id": null
}
},
{
"result": {
"observed_value": 14539
},
"exception_info": {
"raised_exception": false,
"exception_message": null,
"exception_traceback": null
},
"meta": {},
"success": true,
"expectation_config": {
"kwargs": {
"value": 14539
},
"expectation_type": "expect_table_row_count_to_equal",
"meta": {},
"ge_cloud_id": null
}
}
],
"success": true,
"statistics": {
"evaluated_expectations": 2,
"successful_expectations": 2,
"unsuccessful_expectations": 0,
"success_percent": 100.0
}
}
As you can see the defaults are loaded e.g. you will notice "expectation_suite_name": "default",
Can someone show me how you can the default to something else?
I'm re-shaping a JSON file which base content is the output of an AWS API call. From the raw output, I'm currently extracting the fields that I'm interested in the format I want except for a small detail that motivated this question.
Specifically, my input comes from the output of aws rds describe-db-instances command and contains the info of multiple RDS instances. Taking an example with 2 instances, this is how it looks:
{
"DBInstances": [
{
"DBInstanceIdentifier": "db1-name",
"DBInstanceClass": "db.m5.xlarge",
"Engine": "oracle-ee",
"DBInstanceStatus": "available",
"MasterUsername": "user",
"DBName": "RANDOM",
"Endpoint": {
"Address": "some-endpoint.rds.amazonaws.com",
"Port": 5698,
"HostedZoneId": "GHDSFHFSDHSDH"
},
"AllocatedStorage": 4000,
"InstanceCreateTime": "2018-07-23T23:21:42.361000+00:00",
"PreferredBackupWindow": "09:30-07:00",
"BackupRetentionPeriod": 14,
"DBSecurityGroups": [],
"VpcSecurityGroups": [
{
"VpcSecurityGroupId": "sg-xxxxxxxxxxxxxxxxx",
"Status": "active"
},
{
"VpcSecurityGroupId": "sg-xxxxxxxxxxxxxxxxx",
"Status": "active"
}
],
"DBParameterGroups": [
{
"DBParameterGroupName": "DB1-parameter",
"ParameterApplyStatus": "in-sync"
}
],
"AvailabilityZone": "ZONE1",
"DBSubnetGroup": {
"DBSubnetGroupName": "dbsubnetgroup-1",
"DBSubnetGroupDescription": "dbsubnetgroup-1",
"VpcId": "vpc-xxxxxxxxxxxxxxxxx",
"SubnetGroupStatus": "Complete",
"Subnets": [
{
"SubnetIdentifier": "subnet-xxxxxxxxxxxxxxxxx",
"SubnetAvailabilityZone": {
"Name": "az1"
},
"SubnetStatus": "Active"
},
{
"SubnetIdentifier": "subnet-xxxxxxxxxxxxxxxxx",
"SubnetAvailabilityZone": {
"Name": "az2"
},
"SubnetStatus": "Active"
}
]
},
"PreferredMaintenanceWindow": "sat:07:00-sat:07:30",
"PendingModifiedValues": {},
"LatestRestorableTime": "2020-03-27T18:54:25+00:00",
"MultiAZ": false,
"EngineVersion": "X.X.X",
"AutoMinorVersionUpgrade": false,
"ReadReplicaDBInstanceIdentifiers": [],
"LicenseModel": "bring-your-own-license",
"Iops": 5000,
"OptionGroupMemberships": [
{
"OptionGroupName": "optiongroupName",
"Status": "in-sync"
}
],
"CharacterSetName": "WE8ISO8859P15",
"PubliclyAccessible": false,
"StorageType": "io1",
"DbInstancePort": 0,
"StorageEncrypted": true,
"KmsKeyId": "someKey",
"DbiResourceId": "db-xxxxxxxxxxxxxxxxxxxxxxxxx",
"CACertificateIdentifier": "rds-ca-2019",
"DomainMemberships": [],
"CopyTagsToSnapshot": true,
"MonitoringInterval": 0,
"DBInstanceArn": "someARN",
"IAMDatabaseAuthenticationEnabled": false,
"PerformanceInsightsEnabled": false,
"DeletionProtection": false,
"AssociatedRoles": []
},
{
"DBInstanceIdentifier": "db2-name",
"DBInstanceClass": "db.m5.large",
"Engine": "oracle-ee",
"DBInstanceStatus": "available",
"MasterUsername": "user2",
"DBName": "XXXX",
"Endpoint": {
"Address": "endpoint2.rds.amazonaws.com",
"Port": 8974,
"HostedZoneId": "FASDFDS54FSA"
},
"AllocatedStorage": 100,
"InstanceCreateTime": "2020-04-23T21:38:53.023000+00:00",
"PreferredBackupWindow": "01:00-05:30",
"BackupRetentionPeriod": 35,
"DBSecurityGroups": [],
"VpcSecurityGroups": [
{
"VpcSecurityGroupId": "sg-xxxxxxxxxxxxxxxxx",
"Status": "active"
}
],
"DBParameterGroups": [
{
"DBParameterGroupName": "default",
"ParameterApplyStatus": "in-sync"
}
],
"AvailabilityZone": "AZ-2",
"DBSubnetGroup": {
"DBSubnetGroupName": "subnet-group",
"DBSubnetGroupDescription": "",
"VpcId": "vpc-xxxxxxxxxxxxxxxxx",
"SubnetGroupStatus": "Complete",
"Subnets": [
{
"SubnetIdentifier": "subnet-xxxxxxxxxxxxxxxxx",
"SubnetAvailabilityZone": {
"Name": "AZ-1"
},
"SubnetStatus": "Active"
},
{
"SubnetIdentifier": "subnet-xxxxxxxxxxxxxxxxx",
"SubnetAvailabilityZone": {
"Name": "AZ-2"
},
"SubnetStatus": "Active"
},
{
"SubnetIdentifier": "subnet-xxxxxxxxxxxxxxxxx",
"SubnetAvailabilityZone": {
"Name": "AZ-3"
},
"SubnetStatus": "Active"
}
]
},
"PreferredMaintenanceWindow": "sun:08:39-sun:09:09",
"PendingModifiedValues": {},
"LatestRestorableTime": "2020-07-27T18:53:18+00:00",
"MultiAZ": false,
"EngineVersion": "X.X.X",
"AutoMinorVersionUpgrade": false,
"ReadReplicaDBInstanceIdentifiers": [],
"LicenseModel": "bring-your-own-license",
"Iops": 2000,
"OptionGroupMemberships": [
{
"OptionGroupName": "optiongroup-name",
"Status": "in-sync"
}
],
"CharacterSetName": "AL32UTF8",
"PubliclyAccessible": false,
"StorageType": "io1",
"DbInstancePort": 0,
"StorageEncrypted": true,
"KmsKeyId": "someARN",
"DbiResourceId": "db-xxxxxxxxxxxxxxxxx",
"CACertificateIdentifier": "rds-ca-2019",
"DomainMemberships": [],
"CopyTagsToSnapshot": false,
"MonitoringInterval": 0,
"DBInstanceArn": "someARN",
"IAMDatabaseAuthenticationEnabled": false,
"PerformanceInsightsEnabled": false,
"DeletionProtection": false,
"AssociatedRoles": []
}
]
}
This is my current output:
[
{
"DBInstancePrefix": {
"DBInstanceClass": "db.m5.xlarge",
"DBInstanceIdentifier": "db1-name",
"DBName": "RANDOM",
"DBParameterGroupName": "DB1-parameter",
"DBSubnetGroupName": "dbsubnetgroup-1",
"KmsKeyId": "someKey",
"OptionGroupName": "optiongroupName",
"VpcSecurityGroupIds": [
"sg-xxxxxxxxxxxxxxxxx",
"sg-xxxxxxxxxxxxxxxxx"
]
}
},
{
"DBInstancePrefix": {
"DBInstanceClass": "db.m5.large",
"DBInstanceIdentifier": "db2-name",
"DBName": "XXXX",
"DBParameterGroupName": "default",
"DBSubnetGroupName": "subnet-group",
"KmsKeyId": "someARN",
"OptionGroupName": "optiongroup-name",
"VpcSecurityGroupIds": [
"sg-xxxxxxxxxxxxxxxxx"
]
}
}
]
This is my current JQ filter:
. | [.[] | map(.) | .[] | {DBInstancePrefix: {DBInstanceClass: .DBInstanceClass, DBInstanceIdentifier: .DBInstanceIdentifier, DBName: .DBName, DBParameterGroupName:.DBParameterGroups[].DBParameterGroupName, DBSubnetGroupName: .DBSubnetGroup.DBSubnetGroupName, KmsKeyId:.KmsKeyId, OptionGroupName: .OptionGroupMemberships[].OptionGroupName, VpcSecurityGroupIds: [.VpcSecurityGroups | .[] | .VpcSecurityGroupId] }}]
You can verify it on this snippet on jqplay.org.
What I need is to turn the parent key "DBInstancePrefix" dynamic based on a substring from "DBInstanceIdentifier" key. So for the example names I wrote would be:
"db1-name" >>> "db1"
"db2-name" >>> "db2"
So, my desired output would be:
[
{
"db1": {
"DBInstanceClass": "db.m5.xlarge",
"DBInstanceIdentifier": "db1-name",
"DBName": "RANDOM",
"DBParameterGroupName": "DB1-parameter",
"DBSubnetGroupName": "dbsubnetgroup-1",
"KmsKeyId": "someKey",
"OptionGroupName": "optiongroupName",
"VpcSecurityGroupIds": [
"sg-xxxxxxxxxxxxxxxxx",
"sg-xxxxxxxxxxxxxxxxx"
]
}
},
{
"db2": {
"DBInstanceClass": "db.m5.large",
"DBInstanceIdentifier": "db2-name",
"DBName": "XXXX",
"DBParameterGroupName": "default",
"DBSubnetGroupName": "subnet-group",
"KmsKeyId": "someARN",
"OptionGroupName": "optiongroup-name",
"VpcSecurityGroupIds": [
"sg-xxxxxxxxxxxxxxxxx"
]
}
}
]
Any ideas or suggestions? Thanks for reading.
To manipulate the object key-name as you desire, you can apply the filter operation inside (..). Any operation done inside it, the result is preserved "literally".
Your case demands the DBInstanceIdentifier to be split by - and using the first element in the resultant array.
With that and few other trivial modifications, you need
.DBInstances |
map
(
{
( .DBInstanceIdentifier | split("-")[0] ): {
DBInstanceClass,
DBInstanceIdentifier,
DBName,
DBParameterGroupName:.DBParameterGroups[].DBParameterGroupName,
DBSubnetGroupName: .DBSubnetGroup.DBSubnetGroupName,
KmsKeyId,
OptionGroupName: .OptionGroupMemberships[].OptionGroupName,
VpcSecurityGroupIds: [.VpcSecurityGroups[] | .VpcSecurityGroupId ]
}
}
)
jqplay - Demo
I took the long path and since I couldn't resolve it on the same filter I did another one that takes the output of the first one and thus achieved the desired result. But it is awful compared with the answer selected as the solution.
Based on Inian answer, I did a small modification since my real DBInstanceIdentifier values have the following format:
<name>-db-<environment> and I need DBInstancePrefix becomes <name>-db.
So, my final filter is:
.DBInstances |
map
(
{
( .DBInstanceIdentifier|split("-")[0:2] | join("-") ): {
DBInstanceClass,
DBInstanceIdentifier,
DBName,
DBParameterGroupName:.DBParameterGroups[].DBParameterGroupName,
DBSubnetGroupName: .DBSubnetGroup.DBSubnetGroupName,
KmsKeyId,
OptionGroupName: .OptionGroupMemberships[].OptionGroupName,
VpcSecurityGroupIds: [.VpcSecurityGroups[] | .VpcSecurityGroupId ]
}
}
)
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Parameters": {
"AlternateDomainNames": {
"Description": "CNAMEs (alternate domain names), if any, for the distribution. Example. test.codavel.com",
"Type": "String",
"Default": "test.example.com"
}
},
"Resources" : {
"myDistribution" : {
"Type" : "AWS::CloudFront::Distribution",
"Properties" : {
"DistributionConfig" : {
"Origins" : [ {
"DomainName" : "ELBfor-1234.region.elb.amazonaws.com",
"Id" : "myCustomOrigin",
"CustomOriginConfig" : {
"HTTPPort" : "80",
"HTTPSPort" : "443",
"OriginProtocolPolicy" : "match-viewer",
"OriginSSLProtocols" : [
"TLSv1",
"TLSv1.1",
"TLSv1.2",
"SSLv3"
]
}
} ],
"HttpVersion": "http2",
"Aliases": [
{
"Ref": "AlternateDomainNames"
}
],
"Enabled" : "true",
"Comment" : "example-cdn",
"DefaultCacheBehavior" : {
"TargetOriginId" : "myCustomOrigin",
"SmoothStreaming" : "false",
"AllowedMethods": [
"HEAD",
"GET",
"OPTIONS"
],
"MaxTTL": "31536000",
"MinTTL": "0",
"Compress" : "true",
"ForwardedValues" : {
"QueryString" : "false",
"Cookies" : { "Forward" : "all" }
},
"ViewerProtocolPolicy" : "allow-all"
},
"PriceClass" : "PriceClass_All",
"Restrictions" : {
"GeoRestriction": {
"RestrictionType": "none",
"Locations": []
}
},
"ViewerCertificate": {
"SslSupportMethod": "sni-only",
"AcmCertificateArn" : {
"Fn::Sub": "arn:aws:acm:us-east-1:<ID>:certificate/2345f-534234"
}
}
}
}
}
}
}
Hi Team,
I am using this in my cloudfront template to add my custom SSL on that and it is showing me some an error:- Exactly one of [AcmCertificateArn,CloudFrontDefaultCertificate,IamCertificateId] needs to be specified.
So please let me know how will i add this or if there is any option to add in parameter so that it will list that certificate. Please guide me for the same. This is my certificate ARN - arn:aws:acm:us-east-1::certificate/2345f-534234
ViewerCertificate block should look like this in your case:
"ViewerCertificate": {
"SslSupportMethod": "sni-only",
"AcmCertificateArn": " arn:aws:acm:us-east-1::certificate/2345f-534234"
}
Also what you should always take care is that certificate is provisioned in us-east1 region (yours is, based on the ARN :)
The property you need to use is ViewerCertificate. The configuration within the CloudFormation documentation should help you identify any options you might want to add.
You can add a parameter if you would like to specify the ACM certificate, the type will be a string.
Below is an updated template. You will need to ensure th ACM certificate includes your account id. I have ran this to validate that it builds successfully.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"AlternateDomainNames": {
"Description": "CNAMEs (alternate domain names), if any, for the distribution. Example. test.codavel.com",
"Type": "String",
"Default": "test.example.com"
}
},
"Resources": {
"myDistribution": {
"Type": "AWS::CloudFront::Distribution",
"Properties": {
"DistributionConfig": {
"Origins": [{
"DomainName": "ELBfor-1234.region.elb.amazonaws.com",
"Id": "myCustomOrigin",
"CustomOriginConfig": {
"HTTPPort": "80",
"HTTPSPort": "443",
"OriginProtocolPolicy": "match-viewer",
"OriginSSLProtocols": [
"TLSv1",
"TLSv1.1",
"TLSv1.2",
"SSLv3"
]
}
}],
"ViewerCertificate": {
"SslSupportMethod": "sni-only",
"AcmCertificateArn": "arn:aws:acm:us-east-1::certificate/2345f-534234"
},
"HttpVersion": "http2",
"Aliases": [{
"Ref": "AlternateDomainNames"
}],
"Enabled": "true",
"Comment": "example-cdn",
"DefaultCacheBehavior": {
"TargetOriginId": "myCustomOrigin",
"SmoothStreaming": "false",
"AllowedMethods": [
"HEAD",
"GET",
"OPTIONS"
],
"MaxTTL": "31536000",
"MinTTL": "0",
"Compress": "true",
"ForwardedValues": {
"QueryString": "false",
"Cookies": {
"Forward": "all"
}
},
"ViewerProtocolPolicy": "allow-all"
},
"PriceClass": "PriceClass_All",
"Restrictions": {
"GeoRestriction": {
"RestrictionType": "none",
"Locations": []
}
}
}
}
}
}
}
i am getting an error on the
composer identity request -c PeerAdmin#byfn-network-org1 -u admin -s adminpw -d alice
Error: failed to request identity. Error trying to enroll user and return certificates. Error: Calling enrollment endpoint failed with error [Error: connect ETIMEDOUT 192.168.1.159:7054]
Command failed
I am using the following
hyperledger fabric 1.2
composer v0.20
the ip address of my ca container is 172.19.0.4
so why is it that the error say Error: connect ETIMEDOUT 192.168.1.159:7054]
i have already verified (and verified) all the ca certs and the tls certs for all peers are copied correctly in the byfn-network-org1.json .
Also, the TLS is enabled in the docker-composer-cli.yaml
here is a copy of the connection profile.
cat ./org1/*json
{
"name": "byfn-network",
"x-type": "hlfv1",
"version": "1.0.0",
"client": {
"organization": "Org1",
"connection": {
"timeout": {
"peer": {
"endorser": "1200",
"eventHub": "1200",
"eventReg": "1200"
},
"orderer": "1200"
}
}
},
"channels": {
"mychannel": {
"orderers": [
"orderer.example.com"
],
"peers": {
"peer0.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer0.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
}
}
}
},
"organizations": {
"Org1": {
"mspid": "Org1MSP",
"peers": [
"peer0.org1.example.com",
"peer1.org1.example.com"
],
"certificateAuthorities": [
"ca.org1.example.com"
]
},
"Org2": {
"mspid": "Org2MSP",
"peers": [
"peer0.org2.example.com",
"peer1.org2.example.com"
],
"certificateAuthorities": [
"ca.org2.example.com"
]
}
},
"orderers": {
"orderer.example.com": {
"url": "grpcs://localhost:7050",
"grpcOptions": {
"ssl-target-name-override": "orderer.example.com"
},
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICNjCCAdygAwIBAgIRAObpbDwgoENq9ae9xt2Y7iMwCgYIKoZIzj0EAwIwbDEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRowGAYDVQQDExF0bHNjYS5l\neGFtcGxlLmNvbTAeFw0xODA4MTYwMTI3MTRaFw0yODA4MTMwMTI3MTRaMGwxCzAJ\nBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJh\nbmNpc2NvMRQwEgYDVQQKEwtleGFtcGxlLmNvbTEaMBgGA1UEAxMRdGxzY2EuZXhh\nbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQVSKzVu9jhELWQ83zU\nR2FoiYvVDfIPtoFRgWkwe08DTYH8hbav5IQm2BW9cKpZ681weJCvY40TVO/gwk7H\nk64ho18wXTAOBgNVHQ8BAf8EBAMCAaYwDwYDVR0lBAgwBgYEVR0lADAPBgNVHRMB\nAf8EBTADAQH/MCkGA1UdDgQiBCB4ezOoLazLorKZVaP13YOZtzxObPgXiPkQElmG\nsuJZWDAKBggqhkjOPQQDAgNIADBFAiEArtkBLDF23yKQ+uiC3dPQr3AQjyLDCXn6\nKmGQpnt/S8cCIBj0tQUH6eC4A9O9ESq2bg+z2aTz3KIU8iekQVJVw+gK\n-----END CERTIFICATE-----\n"
}
}
},
"peers": {
"peer0.org1.example.com": {
"url": "grpcs://localhost.com:7051",
"grpcOptions": {
"ssl-target-name-override": "peer0.org1.example.com"
},
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICSjCCAfCgAwIBAgIRAJXaYLgBAwxVBW9CaV+CgyMwCgYIKoZIzj0EAwIwdjEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs\nc2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwODE2MDEyNzE0WhcNMjgwODEzMDEy\nNzE0WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE\nBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0G\nA1UEAxMWdGxzY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABJVVanFTaisufMlNtKJt/hcvFE2aB+3gY0P+3HX+1ePIlbmwh9P4pDhN\ndc+mX7g//gKVV+R7NQnDVdn4mdEasX6jXzBdMA4GA1UdDwEB/wQEAwIBpjAPBgNV\nHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wKQYDVR0OBCIEINdm7BDMYTV9\nRWcVHpgv7ghZ7OWj20Mes6Lie+himHw0MAoGCCqGSM49BAMCA0gAMEUCIQDXf1nh\nIvTzP+qwFnHa8iRvJz72NR8hO0FEKjf78yovYgIgerLVhjW5ayyFy4alXUrGL+oO\n5UrDMBF6mWjEpLHiP3k=\n-----END CERTIFICATE-----\n"
}
},
"peer1.org1.example.com": {
"url": "grpcs://peer1.org1.example.com:8051",
"grpcOptions": {
"ssl-target-name-override": "peer1.org1.example.com"
},
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICSjCCAfCgAwIBAgIRAJXaYLgBAwxVBW9CaV+CgyMwCgYIKoZIzj0EAwIwdjEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs\nc2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwODE2MDEyNzE0WhcNMjgwODEzMDEy\nNzE0WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE\nBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0G\nA1UEAxMWdGxzY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABJVVanFTaisufMlNtKJt/hcvFE2aB+3gY0P+3HX+1ePIlbmwh9P4pDhN\ndc+mX7g//gKVV+R7NQnDVdn4mdEasX6jXzBdMA4GA1UdDwEB/wQEAwIBpjAPBgNV\nHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wKQYDVR0OBCIEINdm7BDMYTV9\nRWcVHpgv7ghZ7OWj20Mes6Lie+himHw0MAoGCCqGSM49BAMCA0gAMEUCIQDXf1nh\nIvTzP+qwFnHa8iRvJz72NR8hO0FEKjf78yovYgIgerLVhjW5ayyFy4alXUrGL+oO\n5UrDMBF6mWjEpLHiP3k=\n-----END CERTIFICATE-----\n"
}
},
"peer0.org2.example.com": {
"url": "grpcs://localhost.com:9051",
"grpcOptions": {
"ssl-target-name-override": "peer0.org2.example.com"
},
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICSjCCAfCgAwIBAgIRAMZ3rMvIJP+ebMYvxTsrqEswCgYIKoZIzj0EAwIwdjEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs\nc2NhLm9yZzIuZXhhbXBsZS5jb20wHhcNMTgwODE2MDEyNzE0WhcNMjgwODEzMDEy\nNzE0WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE\nBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0G\nA1UEAxMWdGxzY2Eub3JnMi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABD9NNQrngFbpYV7CqOMk1yDcVqo5wl4XBiSQ9vVCEcFa5kTRUYX5zJcb\nsp9o0CCyDHOyY3aq9RDUnwyP+0W00qCjXzBdMA4GA1UdDwEB/wQEAwIBpjAPBgNV\nHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wKQYDVR0OBCIEIGqaM0oY+4bk\nMAyHivCN12AAq/52PnpO4cUvQCSSy1XZMAoGCCqGSM49BAMCA0gAMEUCIQCsMWyw\nz/k9prAf043Kv/de0/avLm7EXvNDx1kaXQc9CwIgYuuCxvz8z1BdV7Ib/+knTcnX\n9qkKVLGC/L/opKL/mYM=\n-----END CERTIFICATE-----\n"
}
},
"peer1.org2.example.com": {
"url": "grpcs://localhost.com:10051",
"grpcOptions": {
"ssl-target-name-override": "peer1.org2.example.com"
},
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICSjCCAfCgAwIBAgIRAMZ3rMvIJP+ebMYvxTsrqEswCgYIKoZIzj0EAwIwdjEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs\nc2NhLm9yZzIuZXhhbXBsZS5jb20wHhcNMTgwODE2MDEyNzE0WhcNMjgwODEzMDEy\nNzE0WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE\nBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0G\nA1UEAxMWdGxzY2Eub3JnMi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABD9NNQrngFbpYV7CqOMk1yDcVqo5wl4XBiSQ9vVCEcFa5kTRUYX5zJcb\nsp9o0CCyDHOyY3aq9RDUnwyP+0W00qCjXzBdMA4GA1UdDwEB/wQEAwIBpjAPBgNV\nHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMBAf8wKQYDVR0OBCIEIGqaM0oY+4bk\nMAyHivCN12AAq/52PnpO4cUvQCSSy1XZMAoGCCqGSM49BAMCA0gAMEUCIQCsMWyw\nz/k9prAf043Kv/de0/avLm7EXvNDx1kaXQc9CwIgYuuCxvz8z1BdV7Ib/+knTcnX\n9qkKVLGC/L/opKL/mYM=\n-----END CERTIFICATE-----\n"
}
}
},
"certificateAuthorities": {
"ca.org1.example.com": {
"url": "https://localhost:7054",
"caName": "ca-org1"
},
"ca.org2.example.com": {
"url": "https://localhost:8054",
"caName": "ca-org2"
}
}
}
I'm writing tests for Browserstack using nightwatchJS.
My nightwatch.json looks like that:
{
"src_folders": [
"..."
],
"tests_output": "test/tests_output/",
"detailed_output": true,
"selenium": {
"start_process": false,
"host": "hub.browserstack.com",
"port": 80
},
"test_workers": {
"enabled": true,
"workers": 2
},
"test_settings": {
"ie7": {
"selenium_port": 80,
"selenium_host": "hub.browserstack.com",
"silent": true,
"desiredCapabilities": {
"os": "Windows",
"os_version": "XP",
"browserName": "IE",
"version": "7",
"resolution": "1024x768",
"javascriptEnabled": true,
"acceptSslCerts": true,
"browserstack.local": "true",
"browserstack.video": "true",
"browserstack.debug": "true",
"browserstack.localIdentifier": "<localIdentifier>",
"browserstack.user": "<userName>",
"browserstack.key": "<userkey>"
}
},
"ie8": {
"selenium_port": 80,
"selenium_host": "hub.browserstack.com",
"silent": true,
"desiredCapabilities": {
"os": "Windows",
"os_version": "7",
"browserName": "IE",
"version": "8",
"resolution": "1024x768",
"javascriptEnabled": true,
"acceptSslCerts": true,
"browserstack.local": "true",
"browserstack.video": "true",
"browserstack.debug": "true",
"browserstack.localIdentifier": "<localIdentifier>",
"browserstack.user": "<userName>",
"browserstack.key": "<userkey>"
}
},
"chrome": {
"selenium_port": 80,
"selenium_host": "hub.browserstack.com",
"silent": true,
"desiredCapabilities": {
"os": "Windows",
"os_version": "10",
"browserName": "chrome",
"resolution": "1024x768",
"javascriptEnabled": true,
"acceptSslCerts": true,
"browserstack.local": "true",
"browserstack.video": "true",
"browserstack.debug": "true",
"browserstack.localIdentifier": "<localIdentifier>",
"browserstack.user": "<userName>",
"browserstack.key": "<userkey>"
}
},
//Similar blocks for other platforms
}
}
It's the classic way to define a configuration file.
As you can remark there are a lot of redundant information in each platform: localIdentifier, userName, userkey...etc
My question: Is there a way to optimize the configuration file? So when I want for example to change my userKey or browserstack.debug, I change it only in one place and avoid mistakes?
Thanks to the comment of #Mukesh-Tiwari I was able to optimize my nightwatch.json file. I'm sharing the idea:
for(var i in nightwatch_config.test_settings){
var config = nightwatch_config.test_settings[i];
config['selenium_host'] = nightwatch_config.selenium.host;
config['selenium_port'] = nightwatch_config.selenium.port;
config['desiredCapabilities'] = config['desiredCapabilities'] || {};
for(var j in nightwatch_config.common_capabilities){
config['desiredCapabilities'][j] = config['desiredCapabilities'][j] || nightwatch_config.common_capabilities[j];
}
}
In the example above, I'm defining selenium_host, selenium_port at 1 place so if I need to change them I just touch only 1 line of code.
Thanks again for Mukesh-Tiwari.
Imprtant
To make it works, you need to change your configuration file from nightwatch.json to nightwatch.js because we are using a JS code.