Getting a 400 and "Invalid Input: groupDescription" when creating a group with special characters in the name/description - google-admin-sdk

I've tried with the java client, version directory_v1-rev20201215-1.31.0 as well as direct REST calls, and I continue to get a 400 error. I haven't tried a full permutation of special characters, but I know for sure that an equals sign will fail when in the name or description of a group. There's gotta be a simple escaping/encoding that I need to do, but I sure haven't found the part of the documentation that mentions it!
I also know that using the Google UI to create a group works, so IT is doing the right thing to allow special characters through.
Here's some pretty stripped down code, in case it helps:
public Group createGroup() {
Directory directoryService = null;
try {
final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
final JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleCredentials credentials;
try (FileInputStream serviceAccountStream = new FileInputStream(CREDENTIALS_FILE_PATH)) {
ServiceAccountCredentials saCredentials = ServiceAccountCredentials.fromStream(serviceAccountStream);
credentials = saCredentials.createDelegated(IMPERSONATION_ACCOUNT).createScoped(DirectoryScopes.ADMIN_DIRECTORY_GROUP);
}
directoryService = new Directory.Builder(httpTransport, jsonFactory, new HttpCredentialsAdapter(credentials))
.build();
} catch (GeneralSecurityException | IOException e) {
log.error("Unable to initialize service", e);
}
Group group = null;
try {
String groupText = "ASDF = QWERTY";
String email = "foo#bar.com";
Group newGroup = new Group();
newGroup.setName(groupText);
newGroup.setDescription(groupText);
newGroup.setEmail(email);
group = directoryService.groups().insert(newGroup).execute();
} catch (IOException e) {
log.error("uh oh", e);
}
return group;
}
I've also tried various other escaping and encoding mechanisms, but everything still stays encoded when returning the group. So, apparently I still haven't found the right one. Any thoughts/tips?
Update 1:
In response to #ron-m below, I am able to create a group with those special characters via the admin interface:
{
"kind": "admin#directory#group",
"id": "01fob9te1iak5t4",
"etag": "\"TMv00O2ISW7vEPqlqGLVmYmFaDFMCNc2QQc0SLgd5dQ/i_JUX6L9OJm-W4jcyL1hB1TApGg\"",
"email": "__-cwm--test--group--characters-iu-group#XXXXXXXXX",
"name": "__ CWM < test = group > characters",
"directMembersCount": "1",
"description": "__ CWM < test = group > characters, but in the description",
"adminCreated": false
}
Chris

Name guidelines for groups
When adding group names, and group descriptions to Google Workspace or another Google Cloud account, use the following guidelines
Group names and descriptions
Names can be up to 73 characters long.
Use names that make it easy to identify the group’s purpose.
For groups created in the Admin console, names and descriptions can’t contain equal signs (=), or brackets (<,>).
This guidelines seems to be applicable as well when creating groups using groups.insert().
I was able to create a group with special character other than the mentioned invalid characters (<,>,=).
Sample Response Body:
{
"kind": "admin#directory#group",
"id": "sample id",
"etag": "sample tag"",
"email": "sample#example.com",
"name": "ASDF, QWERTY",
"description": "",
"adminCreated": true
}
Admin Console:

Related

AWS Kendra PreHook Lambdas for Data Enrichment

I am working on a POC using Kendra and Salesforce. The connector allows me to connect to my Salesforce Org and index knowledge articles. I have been able to set this up and it is currently working as expected.
There are a few custom fields and data points I want to bring over to help enrich the data even more. One of these is an additional answer / body that will contain key information for the searching.
This field in my data source is rich text containing HTML and is often larger than 2048 characters, a limit that seems to be imposed in a String data field within Kendra.
I came across two hooks that are built in for Pre and Post data enrichment. My thought here is that I can use the pre hook to strip HTML tags and truncate the field before it gets stored in the index.
Hook Reference: https://docs.aws.amazon.com/kendra/latest/dg/API_CustomDocumentEnrichmentConfiguration.html
Current Setup:
I have added a new field to the index called sf_answer_preview. I then mapped this field in the data source to the rich text field in the Salesforce org.
If I run this as is, it will index about 200 of the 1,000 articles and give an error that the remaining articles exceed the 2048 character limit in that field, hence why I am trying to set up the enrichment.
I set up the above enrichment on my data source. I specified a lambda to use in the pre-extraction, as well as no additional filtering, so run this on every article. I am not 100% certain what the S3 bucket is for since I am using a data source, but it appears to be needed so I have added that as well.
For my lambda, I create the following:
exports.handler = async (event) => {
// Debug
console.log(JSON.stringify(event))
// Vars
const s3Bucket = event.s3Bucket;
const s3ObjectKey = event.s3ObjectKey;
const meta = event.metadata;
// Answer
const answer = meta.attributes.find(o => o.name === 'sf_answer_preview');
// Remove HTML Tags
const removeTags = (str) => {
if ((str===null) || (str===''))
return false;
else
str = str.toString();
return str.replace( /(<([^>]+)>)/ig, '');
}
// Truncate
const truncate = (input) => input.length > 2000 ? `${input.substring(0, 2000)}...` : input;
let result = truncate(removeTags(answer.value.stringValue));
// Response
const response = {
"version" : "v0",
"s3ObjectKey": s3ObjectKey,
"metadataUpdates": [
{"name":"sf_answer_preview", "value":{"stringValue":result}}
]
}
// Debug
console.log(response)
// Response
return response
};
Based on the contract for the lambda described here, it appears pretty straight forward. I access the event, find the field in the data called sf_answer_preview (the rich text field from Salesforce) and I strip and truncate the value to 2,000 characters.
For the response, I am telling it to update that field to the new formatted answer so that it complies with the field limits.
When I log the data in the lambda, the pre-extraction event details are as follows:
{
"s3Bucket": "kendrasfdev",
"s3ObjectKey": "pre-extraction/********/22736e62-c65e-4334-af60-8c925ef62034/https://*********.my.salesforce.com/ka1d0000000wkgVAAQ",
"metadata": {
"attributes": [
{
"name": "_document_title",
"value": {
"stringValue": "What majors are under the Exploratory track of Health and Life Sciences?"
}
},
{
"name": "sf_answer_preview",
"value": {
"stringValue": "A complete list of majors affiliated with the Exploratory Health and Life Sciences track is available online. This track allows you to explore a variety of majors related to the health and life science professions. For more information, please visit the Exploratory program description. "
}
},
{
"name": "_data_source_sync_job_execution_id",
"value": {
"stringValue": "0fbfb959-7206-4151-a2b7-fce761a46241"
}
},
]
}
}
The Problem:
When this runs, I am still getting the same field limit error that the content exceeds the character limit. When I run the lambda on the raw data, it strips and truncates it as expected. I am thinking that the response in the lambda for some reason isn't setting the field value to the new content correctly and still trying to use the data directly from Salesforce, thus throwing the error.
Has anyone set up lambdas for Kendra before that might know what I am doing wrong? This seems pretty common to be able to do things like strip PII information before it gets indexed, so I must be slightly off on my setup somewhere.
Any thoughts?
since you are still passing the rich text as a metadata filed of a document, the character limit still applies so the document would fail at validation step of the API call and would not reach the enrichment step. A work around is to somehow append those rich text fields to the body of the document so that your lambda can access it there. But if those fields are auto generated for your documents from your data sources, that might not be easy.

Setting label on Google Compute Engine instance disk

I'm going to try to set a disk label on a Google Compute Engine instance. Basically what is documentated here:
https://cloud.google.com/compute/docs/reference/rest/v1/disks/setLabels
Unfortunately also using the simple code provided by Google:
require_once __DIR__ . '/vendor/autoload.php';
$client = new Google_Client();
$client->setApplicationName('Google-ComputeSample/0.1');
$client->useApplicationDefaultCredentials();
$client->addScope('https://www.googleapis.com/auth/cloud-platform');
$service = new Google_Service_Compute($client);
$project = 'my-project';
$zone = 'my-zone';
$resource = 'my-resource'; // here i set the disk name
$requestBody = new Google_Service_Compute_ZoneSetLabelsRequest();
$response = $service->disks->setLabels($project, $zone, $resource, $requestBody);
echo '<pre>', var_export($response, true), '</pre>', "\n";
?>
I always hit a 500 error:
Uncaught Google_Service_Exception: { "error": { "errors": [ { "domain": "global", "reason": "conditionNotMet", "message": "Labels fingerprint either invalid or resource labels have changed", "locationType": "header", "location": "If-Match" } ], "code": 412, "message": "Labels fingerprint either invalid or resource labels have changed" } }
where I suppose that I have the wrong label syntax. But in the label method, I have tried several syntax:
$requestBody->setLabels(array("mylabel"=>"1"));
$requestBody->setLabels(serialize(array("mylabel"=>"1")));
$requestBody->setLabels('"mylabel":"1"');
$requestBody->setLabels('{"mylabel":"1"}');
but none work. And nothing has changed (always 500 error with the same exception). What did I do wrong?
The error response you're receiving indicates that the labelFingerprint is wrong or not set. The request body should contain both the labels and the labelFingerprint and it looks like you're only setting the former:
{
"labels": {
string: string,
...
},
"labelFingerprint": string
}
The documentation you linked explains what the lableFingerprint is:
The fingerprint of the previous set of labels for this resource, used to detect conflicts. The fingerprint is initially generated by Compute Engine and changes after every request to modify or update labels. You must always provide an up-to-date fingerprint hash in order to update or change labels. Make a get() request to the resource to get the latest fingerprint.
A base64-encoded string.

Terraform random_string as password for linux machine

I want to use this to generate a random password for the linux machines in gcp.
My question is how I get the password afterwards.
Should I use the output for this or is it stored anywhere else?
I saw this code on the internet and was asking myself how they know the password then.
resource "random_string" "master_password" {
length = 16
special = true
}
resource "google_container_cluster" "test" {
name = "test"
zone = "europe-west1-d"
master_auth {
username = "client"
password = "${random_string.master_password.result}"
}
node_pool = [{
name = "pool"
autoscaling = {
min_node_count = 1
max_node_count = 3
}
node_config {
disk_size_gb = 100
machine_type = "n1-standard-2"
oauth_scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
]
labels {
test = "true"
}
}
}]
}
The password will be stored in your state file. You can go digging around in there for it, but it is entirely possible that it's exact location in the file will change between Terraform versions.
The best way to get a consistent output is to, as you mentioned, use an output block. Then when you do a terraform apply there will be a nice human readable output of this password. Please note that anything with access to your state has access to that password, so keep the state secure.
If you use remote state (such as in an S3 bucket), you can also use terraform_remote_state to gain access to this from another Terraform run. You will need to explicitly output values you want to be available from terraform_remote_state.
Finally, be aware that if something captures the output of your terraform apply, it will also capture that output since terraform apply writes to STDOUT. This might happen if you use a CI tool. Just something to be aware of.

I want to manipulate the file in MarkLogic using Python

declareUpdate();
//get Docs
myDoc = cts.doc("/heal/scripts/Test.json").toObject();
//add Data
myDoc.prescribedPlayer =
[
{
"default": "http://www.youtube.com/watch?vu003dhYB0mn5zh2c"
}
]
//persist
xdmp.documentInsert("/heal/scripts/Test.json",myDoc,null,"scripts")
You're looking to add a new JSON property. You can do that using a REST Client API request, sending a PATCH command. Use an insert instruction in the patch.
See the note in Specifying Position in JSON, which indicates that
You cannot use last-child to insert a property as an immediate child of the root node of a document. Use before or after instead. For details, see Limitations of JSON Path Expressions.
Instead, your patch will look something like:
{
"insert": {
"context": "/topProperty",
"position": "after",
"content":
[
{
"default": "http://www.youtube.com/watch?vu003dhYB0mn5zh2c"
}
],
}
}
where topProperty is a JSON property that is part of the root node of the JavaScript object you want to update.
If that approach is problematic (for instance, if there is no topProperty that's reliably available), you could also do a sequence of operations:
retrieve the document
edit the content in Python
update the document in the database
With this approach, there is the possibility that some other process may update the document while you're working on it. You can either rely on optimistic locking or a multi-statement transaction to work around that, depending on the potential consequences of someone else doing a write.
Hey #Ankur Please check below python method,
def PartialUpdateData(self,filename, content, context):
self.querystring = {"uri": "/" + self.collection + "/" + filename}
url = self.baseUri
self.header = {'Content-Type': "application/json"}
mydata = {
"patch":[{ "insert": {
"context": context,
"position": "before",
"content": content
}}]}
resp = requests.patch(url + "/documents", data=json.dumps(mydata),
headers=self.header, auth=self.auth, params=self.querystring)
return resp.content
I hope this can solve your problem.

Entries in me/feed from current user to group

Recently me/feed started to include entries sent by current user to groups.
The id field is invalid for those entries and simply returns "false".
Changing the first part of the id to the one mentioned in "to" gives a valid entry.
It could be a good workaround except there is no way to immediately know which posts are normal and which posts are to Groups.
I did notice that on posts to groups there is an extra field 'version' that is not documented. Anyone knows what this field means?
Example entry:
...
"id": "1188060277_343671429042688",
"from": {
"name": "My Name",
"id": "1188060277"
},
"to": {
"data": [
{
"version": 1,
"name": "Group Name",
"id": "194744830602016"
}
]
},
...
Hope this helps :)
groups
The Groups that the user belongs to.
user_groups or friends_groups.
An array of objects containing the version(old-0 or new Group-1), name, id, administrator (if user is the administrator of the Group) and bookmark_order(at what place in the list of group bookmarks on the homepage, the group shows up for the user).
http://developers.facebook.com/docs/reference/api/user/