Parse.Query.Exclude() function not excluding the given values - parse-platform

I am trying to write cloud code that excludes certain values from a query. I have tried using both select() and exclude() to limit what values come back in a query. Here is what my code looks like
Parse.Cloud.beforeFind(Parse.User, (request) => {
return request.query.select('email');
});
For reference, here is the curl command that I am using to test my cloud code:
curl --location --request GET 'https://<parse-server>.b4a.io/parse/users' \
--header 'X-Parse-Application-Id: <app-id>' \
--header 'X-Parse-REST-API-Key: <api-key>' \
--header 'X-Parse-Session-Token: <token>'
In theory, the response to this command should look something like this:
{
results:[{'email':'123#example.com'},{'email':'456#example.com'}']
}
Instead what I get is the following(all of the user information I would normally get if I did nothing):
{
"results": [
{
"objectId": "jKXYLgeB6x",
"username": "cperryoh",
"email": "123#example.com",
"isBanned": false,
"emailVerified": true,
"createdAt": "2022-07-25T17:09:37.963Z",
"updatedAt": "2022-07-27T16:34:54.186Z",
"chunksOwned": {
"__type": "Relation",
"className": "chunk"
},
"ACL": {
"jKXYLgeB6x": {
"read": true,
"write": true
},
"*": {
"read": true
}
}
},
{
"objectId": "ykJJhvNuPh",
"username": "testUser",
"email":"456#example.com"
"isBanned": false,
"emailVerified": true,
"createdAt": "2022-07-25T19:39:02.854Z",
"updatedAt": "2022-07-27T16:20:48.083Z",
"chunksOwned": {
"__type": "Relation",
"className": "chunk"
},
"ACL": {
"*": {
"read": true
},
"ykJJhvNuPh": {
"read": true,
"write": true
}
}
}
]
}
To further add to the confusion, in the cloud-code/parse-SDK documentation, there is no reference to the beforeFind trigger. So I am not even sure what the return type of lambda/function is supposed to be. The only thing I have that is telling me I can return a query object is an example in the cloud code guide here. In one of the examples, they return the result of Parse.Query.or() which does return an object of type Query. Am I using .select() incorrectly or is it not meant for what I am trying to do? Thanks for the help!

Try just:
Parse.Cloud.beforeFind(Parse.User, (request) => {
request.query.select('email');
});

Related

How to change the local payload when invoking a lambda in cloud9 IDE?

I'm developing in AWS Cloud9, and have a basic "Hello, World" API set up using Lambda.
Now I would like to iterate so that the API can accept parameters. Cloud9 used to have a convenient UI for modifying the payload when running "local" (in the IDE, without deploy). But I can't find where this has been moved, and the documentation still references the previous UI.
To test this, I've included a simple print(event) in my Lambda, and started modifying various components. So far I only print an empty dict ({}).
I suspect it's in the launch.json but so far everything I've modified has not been picked up. Showing below
{
"configurations": [
{
"type": "aws-sam",
"request": "direct-invoke",
"name": "API token-to-geojson:HelloWorldFunction (python3.9)",
"invokeTarget": {
"target": "api",
"templatePath": "token-to-geojson/template.yaml",
"logicalId": "HelloWorldFunction"
},
"api": {
"path": "/hello",
"httpMethod": "get",
"payload": {
"json": {}
}
},
"lambda": {
"runtime": "python3.9"
}
},
{
"type": "aws-sam",
"request": "direct-invoke",
"name": "token-to-geojson:HelloWorldFunction (python3.9)",
"invokeTarget": {
"target": "template",
"templatePath": "token-to-geojson/template.yaml",
"logicalId": "HelloWorldFunction"
},
"lambda": {
"payload": {
"ticky": "tacky"
},
"environmentVariables": {},
"runtime": "python3.9"
}
}
]
}
The only thing I saw is we need to add "json" before the actual json data. In the example below, it appears the IDE already knows the id is event.id (note event is the first argument of the handler).
"lambda": {
"payload": {
"json": {
"id": 1001
}
},
"environmentVariables": {}
}

DynamoDB DocumentClient returns Set of strings (SS) attribute as an object

I'm new to DynamoDB.
When I read data from the table with AWS.DynamoDB.DocumentClient class, the query works but I get the result in the wrong format.
Query:
{
TableName: "users",
ExpressionAttributeValues: {
":param": event.pathParameters.cityId,
":date": moment().tz("Europe/London").format()
},
FilterExpression: ":date <= endDate",
KeyConditionExpression: "cityId = :param"
}
Expected:
{
"user": "boris",
"phones": ["+23xxxxx999", "+23xxxxx777"]
}
Actual:
{
"user": "boris",
"phones": {
"type": "String",
"values": ["+23xxxxx999", "+23xxxxx777"],
"wrapperName": "Set"
}
}
Thanks!
The [unmarshall] function from the [AWS.DynamoDB.Converter] is one solution if your data comes as e.g:
{
"Attributes": {
"last_names": {
"S": "UPDATED last name"
},
"names": {
"S": "I am the name"
},
"vehicles": {
"NS": [
"877",
"9801",
"104"
]
},
"updatedAt": {
"S": "2018-10-19T01:55:15.240Z"
},
"createdAt": {
"S": "2018-10-17T11:49:34.822Z"
}
}
}
Please notice the object/map {} spec per attribute, holding the attr type.
Means you are using the [dynamodb]class and not the [DynamoDB.DocumentClient].
The [unmarshall] will Convert a DynamoDB record into a JavaScript object.
Stated and backed by AWS. Ref. https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/Converter.html#unmarshall-property
Nonetheless, I faced the exact same use case, as yours. Having one only attribute, TYPE SET (NS) in my case, and I had to manually do it. Next a snippet:
// Please notice the <setName>, which represents your set attribute name
ddbTransHandler.update(params).promise().then((value) =>{
value.Attributes[<setName>] = value.Attributes[<setName>].values;
return value; // or value.Attributes
});
Cheers,
Hamlet

Getting started with Step Functions and I can't get Choice to work correctly

I'm doctoring up my first step function, and as a newb into this I am struggling to make this work right. The documentation on AWS is helpful but lacks examples of what I am trying understand. I found a couple similar issues on the site here, but they didn't really answer my question either.
I have a test Step Function that works really simply. I have a small Lambda function that kicks out a single line JSON with a "Count" from a request in a DynamoDB:
def lambda_handler(event, context):
"""lambda_handler
Keyword arguments:
event -- dict -- A dict of parameters to be validated.
context --
Return:
json object with the hubID from DynamoDB of the new hub.
Exceptions:
None
"""
# Prep the Boto3 resources needed
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('TransitHubs')
# By default we assume there are no new hubs
newhub = { 'Count' : 0 }
# Query the DynamoDB to see if the name exists or not:
response = table.query(
IndexName='Status-index',
KeyConditionExpression=Key('Status').eq("NEW"),
Limit=1
)
if response['Count']:
newhub['Count'] = response['Count']
return json.dumps(newhub)
A normal output would be:
{ "Count": 1 }
And then I create this Step Function:
{
"StartAt": "Task",
"States": {
"Task": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:OMGSUPERSECRET:function:LaunchNode-get_new_hubs",
"TimeoutSeconds": 60,
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Count",
"NumericEquals": 0,
"Next": "Failed"
},
{
"Variable": "$.Count",
"NumericEquals": 1,
"Next": "Succeed"
}
]
},
"Succeed": {
"Type": "Succeed"
},
"Failed": {
"Type": "Fail"
}
}
}
So I kick off the State Function and I get this output:
TaskStateExited
{
"name": "Task",
"output": {
"Count": 1
}
}
ChoiceStateEntered
{
"name": "Choice",
"input": {
"Count": 1
}
}
ExecutionFailed
{
"error": "States.Runtime",
"cause": "An error occurred while executing the state 'Choice' (entered at the event id #7). Invalid path '$.Count': The choice state's condition path references an invalid value."
}
So my question: I don't get why this is failing with that error message. Shouldn't Choice just pick up that value from the JSON? Isn't the default "$" input the "input" path?
I have figured out what the issue is, and here it is:
In my Python code, I was attempting to json.dumps(newhub) for the response thinking that what I needed was a string output representing the json formatted response. But it appears that is incorrect. When I alter the code to be simply "return newhub" and return the DICT, the step-functions process accepts that correctly. I'm assuming it parses the DICT to JSON for me? But the output difference is clearly obvious:
old Task output from above returning json.dumps(newhub):
{
"name": "Task",
"output": {
"Count": 1
}
}
new Task output from above returning newhub:
{
"Count": 1
}
And the Choice now correctly matches the Count variable in my output.
In case this is helpful for someone else. I also experienced the kind of error you did ( you had the below... just copy-pasting yours... )
{
"error": "States.Runtime",
"cause": "An error occurred while executing the state 'Choice' (entered at the event id #7). Invalid path '$.Count': The choice state's condition path references an invalid value."
}
But my problem turned out when I was missing the "Count" key all together.
But I did not want verbose payloads.
But per reading these docs I discovered I can also do...
"Choice": {
"Type": "Choice",
"Choices": [
{
"And": [
{
"Variable": "$.Count",
"IsPresent": true
},
{
"Variable": "$.Count",
"NumericEquals": 0,
}
],
"Next": "Failed"
},
{
"And": [
{
"Variable": "$.Count",
"IsPresent": true
},
{
"Variable": "$.Count",
"NumericEquals": 1,
}
],
"Next": "Succeed"
}
]
},
even I was facing the same issue. Give the ResultPath as InputPath and give the choice value as .value name
{
"Comment": "Step function to execute lengthy synchronous requests",
"StartAt": "StartProcessing",
"States": {
"StartProcessing": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:703569030910:function:shravanthDemo-shravanthGetItems-GHF7ZA1p6auQ",
"InputPath": "$.lambda",
"ResultPath": "$.lambda",
"Next": "VerifyProcessor"
},
"VerifyProcessor": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.lambda.cursor",
"IsNull": false,
"Next": "StartProcessing"
},
{
"Variable": "$.lambda.cursor",
"IsNull": true,
"Next": "EndOfUpdate"
}
]
},
"EndOfUpdate": {
"Type": "Pass",
"Result": "ProcessingComplete",
"End": true
}
}
}

Parse Query by subfield/dot notation

tl;dr
Can ParseCloud/MongoDB filter by Pointer<class>.filed ? By
Pointer<class>.Pointer<class> ? By existence of data in that filed?
Long question:
Round is object which will be played automatically when time will come.
Payment object which indicates that user made payment. When payment being spent we set field round to it.
Player which links online User with Payment
I need to query player for few conditions:
Player
online
has valid(no round and valid equal to 'valid') payment
Player
user equal to specific user
has no payment
Player
user equal to specific user
has valid(no round and valid equal to 'valid') payment
And I made everything to work except validating Payment inside Player query.
Here is condition 1 from the list.
var query = new Parse.Query(keys.Player);
query.skip(0);
query.limit(oneRoundMaxPlayers);
query.greaterThanOrEqualTo(keys.last_online_date, lastAllowedOnline);
// looks like no filter applied here
query.doesNotExist("payment.round");
query.exists(keys.payment);
// This line will make query return 0 elements
// query.equalTo("payment.valid", "valid");
query.include(keys.user);
query.include(keys.payment);
Here is 2 OR 3
var queryPaymentExists = new Parse.Query(keys.Player);
queryPaymentExists.skip(0);
queryPaymentExists.limit(1);
queryPaymentExists.exists(keys.payment);
//This line not filtering
queryPaymentExists.doesNotExist(keys.payment + "." + keys.round);
queryPaymentExists.equalTo(keys.user, user);
// This line makes query always return 0 elements
// queryPaymentExists.equalTo(keys.payment + "." + keys.valid, keys.payment_valid);
var queryPaymentDoesNotExist = new Parse.Query(keys.Player);
queryPaymentDoesNotExist.skip(0);
queryPaymentDoesNotExist.limit(1);
queryPaymentDoesNotExist.doesNotExist(keys.payment);
queryPaymentDoesNotExist.equalTo(keys.user, user);
var compoundQuery = Parse.Query.or(queryPaymentExists, queryPaymentDoesNotExist);
compoundQuery.include(keys.user);
compoundQuery.include(keys.payment);
compoundQuery.include(keys.payment + "." + keys.round);
I've checked logs from Mongo and they looks following
verbose: REQUEST for [GET] /classes/Player: {
"include": "user,payment,payment.round",
"where": {
"$or": [
{
"payment": {
"$exists": true
},
"payment.round": {
"$exists": false
},
"user": {
"__type": "Pointer",
"className": "_User",
"objectId": "ASPKs6UVwb"
}
},
{
"payment": {
"$exists": false
},
"user": {
"__type": "Pointer",
"className": "_User",
"objectId": "ASPKs6UVwb"
}
}
]
}
}
Here is response:
verbose: RESPONSE from [GET] /classes/Player: {
"response": {
"results": [
{
"objectId": "VHU9uwmLA7",
"last_online_date": {
"__type": "Date",
"iso": "2017-10-28T15:15:23.547Z"
},
"user": {
"objectId": "ASPKs6UVwb",
"username": "cn92Ekv5WPJcuHjkmTajmZMDW",
},
"createdAt": "2017-10-22T11:43:16.804Z",
"updatedAt": "2017-10-25T09:23:20.035Z",
"ACL": {
"*": {
"read": true
},
"ASPKs6UVwb": {
"read": true,
"write": true
}
},
"__type": "Object",
"className": "_User"
},
"createdAt": "2017-10-27T21:03:35.442Z",
"updatedAt": "2017-10-28T15:15:23.556Z",
"payment": {
"objectId": "nr7ln7U3eJ",
"payment_date": {
"__type": "Date",
"iso": "2017-10-27T23:42:50.614Z"
},
"user": {
"__type": "Pointer",
"className": "_User",
"objectId": "ASPKs6UVwb"
},
"createdAt": "2017-10-27T23:42:50.624Z",
"updatedAt": "2017-10-28T15:12:30.131Z",
"valid": "valid",
"round": {
"objectId": "jF9gqG4ndh",
"round_date": {
"__type": "Date",
"iso": "2017-10-28T15:12:00.027Z"
},
"createdAt": "2017-10-28T15:11:00.036Z",
"updatedAt": "2017-10-28T15:12:30.108Z",
,
"ACL": {
"*": {
"read": true
}
},
"__type": "Object",
"className": "Round"
},
"ACL": {
"ASPKs6UVwb": {
"read": true
}
},
"__type": "Object",
"className": "Payment"
},
"ACL": {
"ASPKs6UVwb": {
"read": true
}
}
}
]
}
}
You can see that response contains payment.round.
My question is following:
Can ParseCloud/MongoDB filter by Pointer<class>.filed ? By Pointer<class>.Pointer<class> ? By existence of data in that filed?
How can I workaround in situation when I need to check field presence if User can have may Players, User can have many Payments.
UPD
As far as I found mongo should support filtering by "dot notation"
mongodb query by sub-field
So what am I doing wrong?
Short answer:
No
Simplify your data structure
Long answer:
Dot notation can be used to
include documents of pointers, as you already did in your code, e.g. include(keys.user)
filter for properties of fields, e.g. {properyA: 1, propertyB: 2}. All the data is in the field, not in another document in another collection that is referenced by a Parse pointer.
Dot notation cannot be used as filter parameter for referenced pointers in a Parse query. MongoDB also does not support such a filtering, the concept of pointer is one by Parse and not by MongoDB. In a NoSQL environment like MongoDB there are no relations between tables to be used in the query language, as it is not a "relational database" like an SQL database. However Parse provides some comfort of an SQL for simple queries with its concepts of pointer, compoundQuery and matchesKeyInQuery.
If that is not sufficient in your case, simply add the fields to the collection. To the expense that you may have the same fields and data in multiple collections but with the advantage of faster query execution time.
Finding the right data structure is one of the big topics for NoSQL as there is no general right structure. The collections and document structures are basically designed as a trade off between:
execution performance
query necessity / frequency
security (access level)
and data storage size
And they are liquid and can change over time. As your app and its queries mutate you'd also change the data structure if the long term gain is greater than the one time effort.

XCode Bots API configuration edit fails

I want to change via the XCode Bots API the scheme name of a bot. A request like curl -XPATCH -H 'Content-Type: application/json' -H 'x-xcsclientversion: 8' https://localhost:20343/api/bots/botid123 -d '{"name": "myawesomebot2"}' to change the bot name works. But if I try to change the configuration object with a request body like '{"configuration": {"schemeName": "scheme2"}}' it fails.
How can the scheme name be modified?
I finally got this solved and want to note the pieces necessary to make this work and how I found it.
First, as you already noticed, is the need for -H "x-xcsclientversion: #" (note, 6 worked for me, as discovered through a check of Apple's Javascript that receives the PATCH request)
Second, after watching Xcode Server update a bot through Charles, it was seen that the URL needs the following parameter added, ?overwriteBlueprint=true
Third, it seems that the JSON data, at the topmost level as name:, also requires requiresUpgrade=false and type=1 (I found the tool jq to be invaluable)
Fourth, changes to the sourceBlueprint requires configuration.sourceControlBlueprint.DVTSourceControlWorkspaceBlueprintIdentifierKey to receive a new UUID. Easily generated in bash by uuidgen command.
Fifth, a mostly-full bot description must be sent. I grabbed the bot's JSON definition through Apple's API, then modified it using jq (see above), deleting out unnecessary key/value pairs so as to match Xcode Server's own API calls. The necessary key/value pairs appear to be:
name
type
requiresUpgrade
configuration (modify as necessary, but a full configuration must be sent)
group (leave as-is, but send it back through)
Sixth, backslash-escape all forward-slashes in the JSON payload. I've done this in bash with ESCAPED_JSON=${BOT_CONFIG_JSON//\//\\\/} and I send -d "$ESCAPED_JSON" in the curl command.
As such, the full curl command I'm using becomes:
curl -k --request PATCH -H "Content-Type: application/json" -H "x-xcsclientversion: 6" -d "$ESCAPED_JSON" https://<username>:<password>#<your_server_address>:20343/api/bots/<your_bot_id>?overwriteBlueprint=true
And a full JSON definition (remember, it must have forward slashes escaped before sending) looks like this
<!-- language: lang-json -->
"requiresUpgrade": false,
"configuration": {
"triggers": [
{
"phase": 1,
"scriptBody": "<REDACTED>",
"type": 1,
"name": "Update github Pending",
"conditions": {
"status": 2,
"onSuccess": true,
"onAnalyzerWarnings": true,
"onBuildErrors": true,
"onWarnings": true,
"onFailingTests": true
}
},
{
"phase": 2,
"scriptBody": "<REDACTED>",
"type": 1,
"name": "Upload to Beta",
"conditions": {
"status": 2,
"onSuccess": true,
"onAnalyzerWarnings": true,
"onBuildErrors": false,
"onWarnings": true,
"onFailingTests": false
}
},
{
"phase": 2,
"scriptBody": "<REDACTED>",
"type": 1,
"name": "Update github status",
"conditions": {
"status": 2,
"onSuccess": true,
"onAnalyzerWarnings": true,
"onBuildErrors": false,
"onWarnings": true,
"onFailingTests": false
}
}
],
"performsUpgradeIntegration": true,
"disableAppThinning": true,
"deviceSpecification": {
"filters": [
{
"platform": {
"_id": "3c884e2499df662057e8c64580003419",
"displayName": "iOS",
"_rev": "8-51c114fcfc83ea5f36df66f119b34ec8",
"simulatorIdentifier": "com.apple.platform.iphonesimulator",
"identifier": "com.apple.platform.iphoneos",
"buildNumber": "14C89",
"version": "10.2"
},
"filterType": 3,
"architectureType": 0
}
],
"deviceIdentifiers": [
"6d928bd891b83b4b8592aedb42001a97",
"6d928bd891b83b4b8592aedb4200776c",
"fa737f03db7b6c04d4c7f9507100700f"
]
},
"periodicScheduleInterval": 0,
"schemeName": "<REDACTED>",
"codeCoveragePreference": 2,
"performsTestAction": true,
"scheduleType": 3,
"performsArchiveAction": true,
"builtFromClean": 2,
"buildConfiguration": "Release",
"performsAnalyzeAction": true,
"sourceControlBlueprint": {
"DVTSourceControlWorkspaceBlueprintLocationsKey": {
"A2739AD29C3BCDF8619D0305ACFDD0C22AEBDDB1": {
"DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlLockedRevisionLocation",
"DVTSourceControlLocationRevisionKey": "9d38dc7507f0f6ac17072d721893f0021c5282ed"
},
"51DBFAD1848AC646B864BBBEDC625B8BAB305A76": {
"DVTSourceControlBranchIdentifierKey": "<THE BRANCH TO WATCH>",
"DVTSourceControlBranchOptionsKey": 4,
"DVTSourceControlWorkspaceBlueprintLocationTypeKey": "DVTSourceControlBranch"
}
},
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey": "51DBFAD1848AC646B864BBBEDC625B8BAB305A76",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey": {},
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationStrategiesKey": {
"A2739AD29C3BCDF8619D0305ACFDD0C22AEBDDB1": {
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlAuthenticationStrategy"
},
"51DBFAD1848AC646B864BBBEDC625B8BAB305A76": {
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryAuthenticationTypeKey": "DVTSourceControlAuthenticationStrategy"
}
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey": {
"A2739AD29C3BCDF8619D0305ACFDD0C22AEBDDB1": 0,
"51DBFAD1848AC646B864BBBEDC625B8BAB305A76": 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey": "<GENERATE A NEW UUID FOR THIS!!!>",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey": {
"A2739AD29C3BCDF8619D0305ACFDD0C22AEBDDB1": "<REDACTED PATH 1>",
"51DBFAD1848AC646B864BBBEDC625B8BAB305A76": "<REDACTED PATH 2>"
},
"DVTSourceControlWorkspaceBlueprintNameKey": "Cool Blueprint",
"DVTSourceControlWorkspaceBlueprintVersion": 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey": "<REDACTED>.xcworkspace",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey": [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git#github.com:<REDACTED REPO 1>",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "A2739AD29C3BCDF8619D0305ACFDD0C22AEBDDB1"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey": "git#github.com:<REDACTED REPO 2>",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey": "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey": "51DBFAD1848AC646B864BBBEDC625B8BAB305A76"
}
]
},
"exportsProductFromArchive": true,
"weeklyScheduleDay": 0,
"minutesAfterHourToIntegrate": 0,
"testingDestinationType": 0,
"hourOfIntegration": 0,
"testingDeviceIDs": []
},
"group": {
"name": "41A62776-A72E-44C0-BFF0-D91F699BBA6A"
},
"type": 1,
"name": "My Cool Integration Bot"
I hope this helps.

Resources