Base64Encode for AWS not matching Amazon Example - amazon-web-services

I seem to be failing at the first hurdle in my quest for Amazon S3 Browser-Based Uploads.
Using Amazon's example shown here : http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
Using Groovy/Java, I'm unable to encode the example post policy and produce the same encoding as Amazon.
{ "expiration": "2015-12-30T12:00:00.000Z",
"conditions": [
{"bucket": "sigv4examplebucket"},
["starts-with", "$key", "user/user1/"],
{"acl": "public-read"},
{"success_action_redirect": "http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html"},
["starts-with", "$Content-Type", "image/"],
{"x-amz-meta-uuid": "14365123651274"},
{"x-amz-server-side-encryption": "AES256"},
["starts-with", "$x-amz-meta-tag", ""],
{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": "20151229T000000Z" }
]
}
I have this defined as a Groovy multi line string:
String policy_document = '''
{ "expiration": "2015-12-30T12:00:00.000Z",
"conditions": [
{"bucket": "sigv4examplebucket"},
["starts-with", "$key", "user/user1/"],
{"acl": "public-read"},
{"success_action_redirect": "http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html"},
["starts-with", "$Content-Type", "image/"],
{"x-amz-meta-uuid": "14365123651274"},
{"x-amz-server-side-encryption": "AES256"},
["starts-with", "$x-amz-meta-tag", ""],
{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": "20151229T000000Z" }
]
}
'''
I'm then encoding and verifying with an assert which fails.
String base64Policy = (new BASE64Encoder()).encode(policy_document.replaceAll("\n", "").replaceAll("\r", "").getBytes("UTF-8"))
assert 'eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9' == base64Policy
Just to help with readability, when I encode I get :
eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLCAgImNvbmRpdGlvbnMiOiBbICAgIHsiYnVja2V0IjogInNpZ3Y0ZXhhbXBsZWJ1Y2tldCJ9LCAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwgICAgeyJhY2wiOiAicHVibGljLXJlYWQifSwgICAgeyJzdWNjZXNzX2FjdGlvbl9yZWRpcmVjdCI6ICJodHRwOi8vc2lndjRleGFtcGxlYnVja2V0LnMzLmFtYXpvbmF3cy5jb20vc3VjY2Vzc2Z1bF91cGxvYWQuaHRtbCJ9LCAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sICAgIHsieC1hbXotbWV0YS11dWlkIjogIjE0MzY1MTIzNjUxMjc0In0sICAgIHsieC1hbXotc2VydmVyLXNpZGUtZW5jcnlwdGlvbiI6ICJBRVMyNTYifSwgICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sICAgIHsieC1hbXotY3JlZGVudGlhbCI6ICJBS0lBSU9TRk9ETk43RVhBTVBMRS8yMDE1MTIyOS91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0sICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwgICAgeyJ4LWFtei1kYXRlIjogIjIwMTUxMjI5VDAwMDAwMFoiIH0gIF19
But I should be getting :
eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9
I'm hoping a fresh set of eyes will point me in the direction. Many thanks for any advice that can be offered.

The following worked for me in Java. Leave the carriage return and line feeds in place.
String policy_document = "{ \"expiration\": \"2015-12-30T12:00:00.000Z\",\n" +
" \"conditions\": [\n" +
" {\"bucket\": \"sigv4examplebucket\"},\n" +
" [\"starts-with\", \"$key\", \"user/user1/\"],\n" +
" {\"acl\": \"public-read\"},\n" +
" {\"success_action_redirect\": \"http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html\"},\n" +
" [\"starts-with\", \"$Content-Type\", \"image/\"],\n" +
" {\"x-amz-meta-uuid\": \"14365123651274\"},\n" +
" {\"x-amz-server-side-encryption\": \"AES256\"},\n" +
" [\"starts-with\", \"$x-amz-meta-tag\", \"\"],\n" +
"\n" +
" {\"x-amz-credential\": \"AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request\"},\n" +
" {\"x-amz-algorithm\": \"AWS4-HMAC-SHA256\"},\n" +
" {\"x-amz-date\": \"20151229T000000Z\" }\n" +
" ]\n" +
"}";
String base64Policy = (new BASE64Encoder()).encode(policy_document.getBytes());

Thanks for pointing me in the right direction.
The final solution was to add \r\n in that order at the end of each line.
String policy_document = "{ \"expiration\": \"2015-12-30T12:00:00.000Z\",\r\n" +
" \"conditions\": [\r\n" +
" {\"bucket\": \"sigv4examplebucket\"},\r\n" +
" [\"starts-with\", \"$key\", \"user/user1/\"],\r\n" +
" {\"acl\": \"public-read\"},\r\n" +
" {\"success_action_redirect\": \"http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html\"},\r\n" +
" [\"starts-with\", \"$Content-Type\", \"image/\"],\r\n" +
" {\"x-amz-meta-uuid\": \"14365123651274\"},\r\n" +
" {\"x-amz-server-side-encryption\": \"AES256\"},\r\n" +
" [\"starts-with\", \"$x-amz-meta-tag\", \"\"],\r\n" +
"\r\n" +
" {\"x-amz-credential\": \"AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request\"},\r\n" +
" {\"x-amz-algorithm\": \"AWS4-HMAC-SHA256\"},\r\n" +
" {\"x-amz-date\": \"20151229T000000Z\" }\r\n" +
" ]\r\n" +
"}";
It's unfortunate that it needs to be implemented so rigidly and that using a Groovy multi line string cannot accomplish this to keep the markup cleaner.

Related

AWS EventBridge PutEvent Detail Malformed

I have tried both AWS Go SDKs both versions and every time I get an error stating that the Details field is malformed. The Details field accepts a JSON string.
In SDK V2 you basically have a struct for the event
type Event struct {
Details []struct {
Key string `json:"Key"`
Value string `json:"Value"`
} `json:"Details"`
DetailType string `json:"DetailType"`
Source string `json:"Source"`
}
The example then builds up the JSON string with this code
myDetails := "{ "
for _, d := range event.Details {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\","
}
myDetails = myDetails + " }"
And then make the api call
input := &cloudwatchevents.PutEventsInput{
Entries: []types.PutEventsRequestEntry{
{
Detail: &myDetails,
DetailType: &event.DetailType,
Resources: []string{
*lambdaARN,
},
Source: &event.Source,
},
},
}
Basically I get an error saying that the string assigned to the Detail field is malformed. I believe this is because the example code generates a string with a trailing which is not valid . However when you omit the , you get a nil memory reference.
AWS SDK Example
The example for SDK version 1 also generates an error.
Any help would be great
The code on the page you linked is not 100% correct. They actually link the "complete example" at the bottom of the page, which has slightly different code:
https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/gov2/cloudwatch/PutEvent/PutEventv2.go#L108-L117
myDetails := "{ "
for i, d := range event.Details {
if i == (len(event.Details) - 1) {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\""
} else {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\","
}
}
myDetails = myDetails + " }"
But building the JSON like this is not ideal and error-prone, as you already found out.
I'd propose the following:
details := make(map[string]string, len(event.Details))
for _, d := range event.Details {
details[d.Key] = d.Value
}
b, err := json.Marshal(details)
if err != nil {
return
}
fmt.Println(string(b))
Checkout the playground to see it in action:
https://play.golang.com/p/E4ueZLGIKp4

AWS API gateway 403 error for Custom Authorizer

I am using Claudia-api-builder to create and deploy the.
https://github.com/claudiajs/example-projects/tree/master/custom-authorizers
My AWS custom authorizer looks like this :
let jwtDecode = require('jwt-decode');
var generatePolicy = function (authToken, methodArn) {
'use strict';
var tmp = methodArn.split(':'),
apiGatewayArnTmp = tmp[5].split('/'),
awsAccountId = tmp[4],
region = tmp[3],
restApiId = apiGatewayArnTmp[0],
stage = apiGatewayArnTmp[1];
let group = jwtDecode(authToken)["cognito:groups"];
if (group[0] === 'Admin') {
return {
'principalId': authToken.split('-')[0],
'policyDocument': {
'Version': '2012-10-17',
'Statement': [{
'Effect': 'Allow',
'Action': [
'execute-api:Invoke'
],
'Resource': [
'arn:aws:execute-api:' + region + ':' + awsAccountId + ':' + restApiId + '/' + stage + '/GET/citizens',
'arn:aws:execute-api:' + region + ':' + awsAccountId + ':' + restApiId + '/' + stage + '/GET/citizens/{citizenId}/personal-details'
]
}]
}
};
}
exports.auth = function testAuth(event, context, callback) {
'use strict';
console.log('got event', event);
/*
* {
* "type":"TOKEN",
* "authorizationToken":"<Incoming bearer token>",
* "methodArn":"arn:aws:execute-api:<Region id>:<Account id>:<API id>/<Stage>/<Method>/<Resource path>"
* }
*/
if (event && event.authorizationToken && event.methodArn) {
callback(null, generatePolicy(event.authorizationToken, event.methodArn));
} else {
callback('Unauthorized');
}
};
The first API from the resource is working fine, but when I am calling the 2nd API i:e :
'arn:aws:execute-api:' + region + ':' + awsAccountId + ':' + restApiId + '/' + stage + '/GET/citizens/{citizenId}/personal-details'
It is giving me 403 Forbidden with :
{
"Message": "User is not authorized to access this resource"
}
In my case, Authorization Caching is also disabled
Any solution for this issue?
The resource should not be the path of the API Gateway method.
In fact it should be the Arn of the resource. You can get this from the AWS console by performing the following:
Open API Gateway
Select your API Gateway
Click the Resources option
Find your resource (This will be the GET method underneath citizens/{citizenId}/personal-details). Click on it.
There will be an Arn available for you.
When using path based parameters any parameter is replaced by an * so this would become the below.
'arn:aws:execute-api:' + region + ':' + awsAccountId + ':' + restApiId + '/' + stage + '/GET/citizens/*/personal-details'
The solution for me was to make a wildcard resource instead of just passing in event.methodArn into the generateAllow()
a wild card resource looks like this, then you pass it into your generateAllow()
var tmp = event.methodArn.split(':');
var apiGatewayArnTmp = tmp[5].split('/');
var resource = tmp[0] + ':' + tmp[1] + ':' + tmp[2] + ':' + tmp[3] + ':' + tmp[4] + ':' + apiGatewayArnTmp[0] + '/*/*';
generatePolicy("1", 'Allow', resource)
a more in-depth explanation can be found here https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-lambda-authorization-errors/

Repeatedly send API request based on parameters in Postman

I have a database table with approx 4,000 records (currently). A response to an API call (POST, JSON) gives me data of this table for a maximum of 1,000 records per API call. A parameter ‘PageNo’ defines which of the 4,000 records are selected (e.g. PageNo = 1 gives me record 1-1000). The header data of the response includes a ‘PageCount’, in my example 4. I am able to retrieve that ‘PageCount’ and the test below loops through the PageNo (result in Postman Console = 1 2 3 4).
How I can call the same request repeatedly in a loop and use the values of the PageNo (i) as a parameter for that request like so:
{{baseUrl}}/v1/Units/Search?PageNo={{i}}
In my example I would expect the request to run 4 times with PageNo2 = 1, 2, 3, 4.
I am aware that I can use a CSV file and loop through the request in Collection Runner but PageCount changes (i.e. the number of records in the table change) and I need to run this loop frequently so creating a new CSV file for each loop is not really an option.
Postman Test:
pm.environment.set('Headers2', JSON.stringify(pm.response.headers));
var Headers2 = JSON.stringify(pm.response.headers);
pm.environment.set('PageCount2', JSON.parse(Headers2)[10].value);
var i;
for (i = 1; [i] <= pm.environment.get('PageCount2'); i++) {
console.log(i);
postman.setNextRequest('custom fields | json Copy');
}
Postman Request:
{
"Location":"{{TestingLocation}}",
"Fields":[
"StockNo",
"BrandDesc"
],
"Filters": {
"StatusCode":"{{TestingUnitSearchStatusCode}}"
},
"PageSize":1000,
"PageNo" : "{{i}}"
}
With postman.setNextRequest() you can set the calling request as the same request. But you need an exit strategy, otherwise that request would be called infinite times. This only works with the collection runner.
On your first(!) request, store the amount of pages and create a counter.
Increment the counter. If it is smaller than the amount of pages, set the current request as the next request.
Else, do not set a next request. The collection runner will continue with its normal flow. Optionally remove the pages and counter variables.
You can let the request detect that it is its first iteration by not having initialized the amount of pages and counter variables.
Performed a POC for your use-case using postman-echo API.
Pre-req script --> Takes care of the initial request to the endpoint to retrieve the PageSize and set it as an env var. Also initializes the iterationCount to 1 (as env var)
Test Script --> Checks for the current iteration number and performs tests.
Here's a working postman collection.
If you're familiar with newman, you can just run:
newman run <collection-name>
You can also import this collection in Postman app and use collection runner.
{
"info": {
"_postman_id": "1d7669a6-a1a1-4d01-a162-f8675e01d1c7",
"name": "Loop Req Collection",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Run_Req",
"event": [
{
"listen": "test",
"script": {
"id": "dac5e805-548b-4bdc-a26e-f56500414208",
"exec": [
"var iterationCount = pm.environment.get(\"iterationCount\");",
"if (pm.environment.get(\"iterationCount\") <= pm.environment.get(\"pageSize\")) {",
" console.log(\"Iteration Number: \" + iterationCount);",
"",
" pm.test(\"Status code is 200\", function() {",
" pm.response.to.have.status(200);",
" });",
"",
" pm.test(\"Check value of pageNo in body is equal to iterationCount\", function() {",
" var jsonData = pm.response.json();",
" return jsonData.json.PageNo == iterationCount;",
"",
" });",
" iterationCount++;",
" pm.environment.set(\"iterationCount\", iterationCount);",
" postman.setNextRequest('Run_Req');",
"}",
"if (pm.environment.get(\"iterationCount\") > pm.environment.get(\"pageSize\")) {",
" console.log(\"Cleanup\");",
" pm.environment.unset(\"pageSize\");",
" pm.environment.set(\"iterationCount\", 1);",
" postman.setNextRequest('');",
" pm.test(\"Cleanup Success\", function() {",
" if (pm.environment.get(\"pageSize\") == null && pm.environment.get(\"iterationCount\") == null) {",
" return true;",
" }",
" });",
"}"
],
"type": "text/javascript"
}
},
{
"listen": "prerequest",
"script": {
"id": "3d43c6c7-4a9b-46cf-bd86-c1823af5a68e",
"exec": [
"if (pm.environment.get(\"pageSize\") == null) {",
" pm.sendRequest(\"https://postman-echo.com/response-headers?pageSize=4\", function(err, response) {",
" var pageSize = response.headers.get('pageSize');",
" var iterationCount = 1;",
" console.log(\"pre-req:pageSize= \" + pageSize);",
" console.log(\"pre-req:iterationCount= \" + iterationCount);",
" pm.environment.set(\"pageSize\", pageSize);",
" pm.environment.set(\"iterationCount\", iterationCount);",
" });",
"",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"name": "Content-Type",
"value": "application/json",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"Location\":\"{{TestingLocation}}\",\n \"Fields\":[\n \"StockNo\",\n \"BrandDesc\"\n ],\n \"Filters\": {\n \"StatusCode\":\"{{TestingUnitSearchStatusCode}}\"\n },\n \"PageSize\":1000,\n \"PageNo\" : \"{{iterationCount}}\"\n}"
},
"url": {
"raw": "https://postman-echo.com/post",
"protocol": "https",
"host": [
"postman-echo",
"com"
],
"path": [
"post"
]
}
},
"response": []
}
]
}

wxpython: 'Example' object has no attribute 'OnClick'

I am making a GUI of the periodic table of elements with wxpython, I have added all the buttons. So now what I want is when the user selects multiple buttons it should give me output same as the labeled on the button. Trying to run this code but getting the error. Don't know what's wrong, I am doing here.
import wx
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title=title, size=(1000, 800))
self.InitUI()
self.Centre()
self.Show()
def InitUI(self):
p = wx.Panel(self)
gs = wx.GridSizer(11, 18, 5, 1)
A = ["H", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "He", "Li", "Be", " ",
" ",
" ", " ", " ", " ", " ", " ", " ", " ", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", " ", " ", " ", " ", " ",
" ",
" ", " ", " ", " ", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co",
"Ni",
"Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd",
"Ag",
"Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au",
"Hg", "Tl",
"Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh",
"Fl",
"Mc", "Lv", "Ts", "Og",
" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
"La",
"Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", " ", " ", " ", "Ac",
"Th",
"Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", " ", " ", " ", " ", " ", " ",
" ",
" ", " ", "Go", " ", " ", " ", " ", " ", " ", " ", " "]
for i in A:
btn = str(i)
a = wx.Button(self, 10, "str(i)", (20, 20)) # buttons are added
a.myname = "str(i)"
self.Bind(wx.EVT_BUTTON, self.OnClick, a)
print(i)
def OnClick(self, event): # When the button is clicked
name = event.GetEventObject().myname
p.SetSizer(gs)
app = wx.App()
Example(None, title='Grid demo')
app.MainLoop()
Well, it seems that the third argument in self.Bind(wx.EVT_BUTTON, self.OnClick, a) is useless, try self.Bind(wx.EVT_BUTTON, self.OnClick)
(comparing with code generated thanks to wxformbuilder)
It's all a bit tight given the number of buttons and you must declare the buttons as part of the panel, not hope that the sizer will do the work for you.
There are few changes but they are important.
Code now includes elements{dictionary} for element display (I'm missing a few of the rarer elements like Oganesson but hey if you can't eat it, smoke it, make a purchase with it or use it to scale a mountain, who cares?)
import wx
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title = title,size = (2000,1000))
self.InitUI()
self.Layout()
self.Centre()
self.Show()
def InitUI(self):
self.elements = {'Ru': 'Ruthenium', 'Re': 'Rhenium', 'Rf': 'Rutherfordium', 'Rg': 'Roentgenium', 'Ra': 'Radium', 'Rb': 'Rubidium', 'Rn': 'Radon', 'Rh': 'Rhodium', 'Be': 'Beryllium', 'Ba': 'Barium', 'Bh': 'Bohrium', 'Bi': 'Bismuth', 'Bk': 'Berkelium', 'Br': 'Bromine', 'H': 'Hydrogen', 'P': 'Phosphorus', 'Os': 'Osmium', 'Es': 'Einsteinium', 'Hg': 'Mercury', 'Ge': 'Germanium', 'Gd': 'Gadolinium', 'Ga': 'Gallium', 'Pr': 'Praseodymium', 'Pt': 'Platinum', 'Pu': 'Plutonium', 'C': 'Carbon', 'Pb': 'Lead', 'Pa': 'Protactinium', 'Pd': 'Palladium', 'Cd': 'Cadmium', 'Po': 'Polonium', 'Pm': 'Promethium', 'Hs': 'Hassium', 'Uup': 'Ununpentium', 'Uus': 'Ununseptium', 'Uuo': 'Ununoctium', 'Ho': 'Holmium', 'Hf': 'Hafnium', 'K': 'Potassium', 'He': 'Helium', 'Md': 'Mendelevium', 'Mg': 'Magnesium', 'Mo': 'Molybdenum', 'Mn': 'Manganese', 'O': 'Oxygen', 'Mt': 'Meitnerium', 'S': 'Sulfur', 'W': 'Tungsten', 'Zn': 'Zinc', 'Eu': 'Europium', 'Zr': 'Zirconium', 'Er': 'Erbium', 'Ni': 'Nickel', 'No': 'Nobelium', 'Na': 'Sodium', 'Nb': 'Niobium', 'Nd': 'Neodymium', 'Ne': 'Neon', 'Np': 'Neptunium', 'Fr': 'Francium', 'Fe': 'Iron', 'Fl': 'Flerovium', 'Fm': 'Fermium', 'B': 'Boron', 'F': 'Fluorine', 'Sr': 'Strontium', 'N': 'Nitrogen', 'Kr': 'Krypton', 'Si': 'Silicon', 'Sn': 'Tin', 'Sm': 'Samarium', 'V': 'Vanadium', 'Sc': 'Scandium', 'Sb': 'Antimony', 'Sg': 'Seaborgium', 'Se': 'Selenium', 'Co': 'Cobalt', 'Cn': 'Copernicium', 'Cm': 'Curium', 'Cl': 'Chlorine', 'Ca': 'Calcium', 'Cf': 'Californium', 'Ce': 'Cerium', 'Xe': 'Xenon', 'Lu': 'Lutetium', 'Cs': 'Caesium', 'Cr': 'Chromium', 'Cu': 'Copper', 'La': 'Lanthanum', 'Li': 'Lithium', 'Lv': 'Livermorium', 'Tl': 'Thallium', 'Tm': 'Thulium', 'Lr': 'Lawrencium', 'Th': 'Thorium', 'Ti': 'Titanium', 'Te': 'Tellurium', 'Tb': 'Terbium', 'Tc': 'Technetium', 'Ta': 'Tantalum', 'Yb': 'Ytterbium', 'Db': 'Dubnium', 'Dy': 'Dysprosium', 'Ds': 'Darmstadtium', 'I': 'Iodine', 'U': 'Uranium', 'Y': 'Yttrium', 'Ac': 'Actinium', 'Ag': 'Silver', 'Uut': 'Ununtrium', 'Ir': 'Iridium', 'Am': 'Americium', 'Al': 'Aluminium', 'As': 'Arsenic', 'Ar': 'Argon', 'Au': 'Gold', 'At': 'Astatine', 'In': 'Indium'}
p = wx.Panel(self)
gs = wx.GridSizer(11, 18, 5, 5)
A = [ "H"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ", "He", "Li", "Be"," "," "," "," "," "," "," "," "," "," ", "B" , "C", "N", "O", "F" , "Ne", "Na", "Mg"," "," "," "," "," "," "," "," "," "," ", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca","Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru","Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "" , "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "", "Rf", "Db", "Sg", "Bh", "Hs","Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ", " ", " "," ", " ", " ", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm","Yb", "Lu"," ", " ", " ","Ac", "Th" , "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk","Cf", "Es", "Fm", "Md" , "No", "Lr"," "," "," "," "," "," "," "," "," " ,"Go"," "," "," "," "," "," "," ","ZZ" ]
for i in A:
btn = wx.Button(p, -1, i, (10,20)) #buttons are added
btn.myname = i
gs.Add(btn,0,)
self.Bind(wx.EVT_BUTTON, self.OnClick, btn)
p.SetSizer(gs)
def OnClick(self, event): #When the button is clicked
name = event.GetEventObject().myname
try:
element = self.elements[name]
wx.MessageBox("You selected element "+element, "Selection",wx.ICON_INFORMATION | wx.OK)
except:
wx.MessageBox("You selected element "+name, "Selection",wx.ICON_INFORMATION | wx.OK)
app = wx.App()
Example(None, title = 'Grid demo')
app.MainLoop()
New version based on your other question Error with on click event in wxpython
import wx
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title = title,size = (1400,500))
self.InitUI()
self.Layout()
self.Centre()
self.Show()
def InitUI(self):
self.elements = {'Ru': 'Ruthenium', 'Re': 'Rhenium', 'Rf': 'Rutherfordium', 'Rg': 'Roentgenium', 'Ra': 'Radium', 'Rb': 'Rubidium', 'Rn': 'Radon', 'Rh': 'Rhodium', 'Be': 'Beryllium', 'Ba': 'Barium', 'Bh': 'Bohrium', 'Bi': 'Bismuth', 'Bk': 'Berkelium', 'Br': 'Bromine', 'H': 'Hydrogen', 'P': 'Phosphorus', 'Os': 'Osmium', 'Es': 'Einsteinium', 'Hg': 'Mercury', 'Ge': 'Germanium', 'Gd': 'Gadolinium', 'Ga': 'Gallium', 'Pr': 'Praseodymium', 'Pt': 'Platinum', 'Pu': 'Plutonium', 'C': 'Carbon', 'Pb': 'Lead', 'Pa': 'Protactinium', 'Pd': 'Palladium', 'Cd': 'Cadmium', 'Po': 'Polonium', 'Pm': 'Promethium', 'Hs': 'Hassium', 'Uup': 'Ununpentium', 'Uus': 'Ununseptium', 'Uuo': 'Ununoctium', 'Ho': 'Holmium', 'Hf': 'Hafnium', 'K': 'Potassium', 'He': 'Helium', 'Md': 'Mendelevium', 'Mg': 'Magnesium', 'Mo': 'Molybdenum', 'Mn': 'Manganese', 'O': 'Oxygen', 'Mt': 'Meitnerium', 'S': 'Sulfur', 'W': 'Tungsten', 'Zn': 'Zinc', 'Eu': 'Europium', 'Zr': 'Zirconium', 'Er': 'Erbium', 'Ni': 'Nickel', 'No': 'Nobelium', 'Na': 'Sodium', 'Nb': 'Niobium', 'Nd': 'Neodymium', 'Ne': 'Neon', 'Np': 'Neptunium', 'Fr': 'Francium', 'Fe': 'Iron', 'Fl': 'Flerovium', 'Fm': 'Fermium', 'B': 'Boron', 'F': 'Fluorine', 'Sr': 'Strontium', 'N': 'Nitrogen', 'Kr': 'Krypton', 'Si': 'Silicon', 'Sn': 'Tin', 'Sm': 'Samarium', 'V': 'Vanadium', 'Sc': 'Scandium', 'Sb': 'Antimony', 'Sg': 'Seaborgium', 'Se': 'Selenium', 'Co': 'Cobalt', 'Cn': 'Copernicium', 'Cm': 'Curium', 'Cl': 'Chlorine', 'Ca': 'Calcium', 'Cf': 'Californium', 'Ce': 'Cerium', 'Xe': 'Xenon', 'Lu': 'Lutetium', 'Cs': 'Caesium', 'Cr': 'Chromium', 'Cu': 'Copper', 'La': 'Lanthanum', 'Li': 'Lithium', 'Lv': 'Livermorium', 'Tl': 'Thallium', 'Tm': 'Thulium', 'Lr': 'Lawrencium', 'Th': 'Thorium', 'Ti': 'Titanium', 'Te': 'Tellurium', 'Tb': 'Terbium', 'Tc': 'Technetium', 'Ta': 'Tantalum', 'Yb': 'Ytterbium', 'Db': 'Dubnium', 'Dy': 'Dysprosium', 'Ds': 'Darmstadtium', 'I': 'Iodine', 'U': 'Uranium', 'Y': 'Yttrium', 'Ac': 'Actinium', 'Ag': 'Silver', 'Uut': 'Ununtrium', 'Ir': 'Iridium', 'Am': 'Americium', 'Al': 'Aluminium', 'As': 'Arsenic', 'Ar': 'Argon', 'Au': 'Gold', 'At': 'Astatine', 'In': 'Indium'}
p = wx.Panel(self)
hbox1= wx.BoxSizer(wx.VERTICAL)
self.t1= wx.TextCtrl (p,0,value="",size=(120,30),style=wx.TE_READONLY)
hbox1.Add(self.t1,proportion=0,flag=wx.CENTER)
gs = wx.GridSizer(11, 18, 5, 5)
A = [ "H"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ", "He", "Li", "Be"," "," "," "," "," "," "," "," "," "," ", "B" , "C", "N", "O", "F" , "Ne", "Na", "Mg"," "," "," "," "," "," "," "," "," "," ", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca","Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru","Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "" , "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "", "Rf", "Db", "Sg", "Bh", "Hs","Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ", " ", " "," ", " ", " ", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm","Yb", "Lu"," ", " ", " ","Ac", "Th" , "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk","Cf", "Es", "Fm", "Md" , "No", "Lr"," "," "," "," "," "," "," "," "," " ," "," "," "," "," "," "," "," "," "]
for element in A:
btn = wx.Button(p, -1, element, (10,20)) #buttons are added
btn.name = element
gs.Add(btn,0,)
self.Bind(wx.EVT_BUTTON, self.OnClick, btn)
hbox1.Add(gs,proportion=0)
p.SetSizer(hbox1)
def OnClick(self, event): #When the button is clicked
name = event.GetEventObject().name
try:
element = self.elements[name]
except:
element = name
self.t1.SetValue(element)
app = wx.App()
Example(None, title = 'Element Grid')
app.MainLoop()
This is now the third time that I have asked you to read how StackOverflow works, please read the tour pages. Ultimately, this is for your own good.

Match with multiple conditions in $OR

In MongoDB I have 2 conditions
First condition:
"\"userfullname\":{$regex : \"" + filtertext + "\", $options: 'i'}";
Second condition:
"\"email\":{$regex : \"" + filtertext + "\", $options: 'i'}";
I need to OR condition on this statement so what I did is:
matchquery = matchquery + "\"userfullname\":{$regex : \"" + filtertext + "\", $options: 'i'}"+["%OR:"]+"\"email\":{$regex : \"" + filtertext + "\", $options: 'i'}";
but of course that did not work. How can I achieve this?
Use the Or Operator & create it like this :
var condition1 =
"\"userfullname\":{$regex : \"" + filtertext + "\", $options: 'i'}";
var condition2 = "\"email\":{$regex : \"" + filtertext + "\", $options: 'i'}";
var matchquery = "{ \"$or\" : [ " + condition1 + "," + condition2 +" ]}"
Use the $or operator. You essentially want to create a query which has the form
var matchquery = {
'$or': [
{ 'userfullname': { '$regex': \filtertext\, '$options': 'i' } },
{ 'email': { '$regex': \filtertext\, '$options': 'i' } }
]
}
If filtertext is a variable then you can use RegExp constructor which creates a regular expression object for matching text with a pattern. For example:
var filtertext = "foo";
var rgx = new RegExp(filtertext, "i");
var matchquery = {
"$or": [
{ "userfullname": rgx },
{ "email": rgx }
]
};
To stringify the query, you could try:
var filtertext = "foo";
var rgx = new RegExp(filtertext, "i");
var matchquery = '{ "$or":[{ "userfullname": {"$regex": '+ rgx.toString() +'} },{ "email":{ "$regex": '+ rgx.toString() + '} }] } }';