How do I get Google DLP V2 API to return data with the same level of accuracy as V2Beta? - google-cloud-dlp

I've ported my Java app from the V2Beta version of the API to V2, and my results coming back seem to be less "accurate" than with the V2Beta version.
Names, addresses, zip codes, age, etc don't get de-identified at all. The results I'm seeing with the V2 API are very different from what I was getting with the V2Beta API. Maybe I'm doing something wrong? Given the input "Hello Mr. John S. Smith! This is Mr. Jones writing back with my SSN: 911-87-9111", the only thing that gets de-identified is the SSN digits. I would have expected the names to be de-identified as well.
I'm using Spring to inject stuff like the credentials, etc and there are some Lombok annotations to simplify my life, but the bulk of the code should be pretty straightforward:
import com.google.api.gax.core.CredentialsProvider;
import com.google.cloud.ProjectName;
import com.google.cloud.dlp.v2.DlpServiceClient;
import com.google.cloud.dlp.v2.DlpServiceSettings;
import com.google.privacy.dlp.v2.CharacterMaskConfig;
import com.google.privacy.dlp.v2.ContentItem;
import com.google.privacy.dlp.v2.DeidentifyConfig;
import com.google.privacy.dlp.v2.DeidentifyContentRequest;
import com.google.privacy.dlp.v2.DeidentifyContentResponse;
import com.google.privacy.dlp.v2.FieldId;
import com.google.privacy.dlp.v2.InfoTypeTransformations;
import com.google.privacy.dlp.v2.InfoTypeTransformations.InfoTypeTransformation;
import com.google.privacy.dlp.v2.PrimitiveTransformation;
import com.google.privacy.dlp.v2.Table;
import com.google.privacy.dlp.v2.Table.Row;
import com.google.privacy.dlp.v2.Value;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.springframework.util.CollectionUtils.isEmpty;
#Service("DeIdentifyTest")
#FieldDefaults(level = AccessLevel.PRIVATE)
#Setter
#Slf4j
public class DeIdentifyTest {
final DlpServiceSettings settings;
final String projectId;
#SneakyThrows
public DeIdentifyTest(CredentialsProvider credentialsProvider, String projectId) {
this.settings = DlpServiceSettings.newBuilder().setCredentialsProvider(credentialsProvider).build();
this.projectId = projectId;
}
public CompletableFuture<Collection<String>> redact(final Collection<String> input,
final String mask) {
return CompletableFuture.supplyAsync(() -> redactContent(input, mask));
}
#SneakyThrows
private Collection<String> redactContent(Collection<String> input, String mask) {
log.debug("Input: {}", input);
if (isEmpty(input)) {
return input;
}
CharacterMaskConfig characterMaskConfig =
CharacterMaskConfig.newBuilder().setMaskingCharacter(mask).build();
PrimitiveTransformation primitiveTransformation =
PrimitiveTransformation.newBuilder().setCharacterMaskConfig(characterMaskConfig).build();
InfoTypeTransformation infoTypeTransformationObject =
InfoTypeTransformation.newBuilder().setPrimitiveTransformation(primitiveTransformation).build();
InfoTypeTransformations infoTypeTransformationArray =
InfoTypeTransformations.newBuilder().addTransformations(infoTypeTransformationObject).build();
DeidentifyConfig deidentifyConfig =
DeidentifyConfig.newBuilder().setInfoTypeTransformations(infoTypeTransformationArray).build();
try (DlpServiceClient dlpClient = DlpServiceClient.create(settings)) {
// Create the deidentification request object
DeidentifyContentRequest request =
DeidentifyContentRequest.newBuilder()
.setParent(ProjectName.of(projectId).toString())
.setDeidentifyConfig(deidentifyConfig)
.setItem(createContentItemWithTable(input))
.build();
// Execute the deidentification request
DeidentifyContentResponse response = dlpClient.deidentifyContent(request);
Table table = response.getItem().getTable();
return Stream.of(table.getRowsList())
.flatMap(rows -> rows.stream())
.flatMap(row -> row.getValuesList().stream())
.map(val -> val.getStringValue())
.collect(Collectors.toCollection(LinkedList::new));
}
}
private ContentItem createContentItemWithTable(Collection<String> input) {
Table.Builder tableBuilder = Table.newBuilder().addHeaders(FieldId.newBuilder().setName("unused").build());
Value.Builder valueBuilder = Value.newBuilder();
Optional<Table.Builder> tableOpt = input.stream()
.filter(item -> isNotBlank(item))
.map(item -> valueBuilder.setStringValue(item).build())
.map(value -> Row.newBuilder().addValues(value).build())
.map(row -> tableBuilder.addRows(row))
.reduce((t1, t2) -> t1);
return ContentItem.newBuilder().setTable(tableOpt.get().build()).build();
}
}

Your example fails to show us what InfoTypes you are choosing to detect. The main thing that changed in V2 is that there is no longer a default list of detectors. You must specify specifically what you are looking for.
See https://cloud.google.com/dlp/docs/infotypes-reference for the entire list.
If I send this
{
"item": {
"value": "Hello Mr. John S. Smith! This is Mr. Jones writing back with my SSN: 509-03-2530"
},
"inspectConfig": {
"includeQuote": true,
"infoTypes": [
{
"name": "PERSON_NAME"
},
{
"name": "US_SOCIAL_SECURITY_NUMBER"
}
]
}
}
I get
{
"result": {
"findings": [
{
"quote": "Mr. John S. Smith",
"infoType": {
"name": "PERSON_NAME"
},
"likelihood": "LIKELY",
"location": {
"byteRange": {
"start": "6",
"end": "23"
},
"codepointRange": {
"start": "6",
"end": "23"
}
},
"createTime": "2018-05-21T16:11:54.449Z"
},
{
"quote": "Jones",
"infoType": {
"name": "PERSON_NAME"
},
"likelihood": "POSSIBLE",
"location": {
"byteRange": {
"start": "37",
"end": "42"
},
"codepointRange": {
"start": "37",
"end": "42"
}
},
"createTime": "2018-05-21T16:11:54.449Z"
},
{
"quote": "509-03-2530",
"infoType": {
"name": "US_SOCIAL_SECURITY_NUMBER"
},
"likelihood": "LIKELY",
"location": {
"byteRange": {
"start": "69",
"end": "80"
},
"codepointRange": {
"start": "69",
"end": "80"
}
},
"createTime": "2018-05-21T16:11:54.425Z"
}
]
}
}

Related

How do I get the path of a downloaded deno module?

Given a downloaded deno module like:
import x from "https://deno.land/x/reggi#0.0.1/calendar/denodb/fresh/island.ts"
How can I find where this specific module is located on the host machine?
➜ deno.land cd x
cd: no such file or directory: x
➜ deno.land pwd
/Users/thomasreggi/Library/Caches/deno/deps/https/deno.land
There seems to be no "x" dir here πŸ‘†
Where is the file stored locally?
How can I create the local file path from url via code / api?
Notes:
The Linking to Third Party Code section in the manual will be a great entrypoint reference on this topic.
The below information was written when Deno's latest release was version 1.25.1.
Deno has a dependency inspector tool:
deno info [URL] will inspect an ES module and all of its dependencies.
This tool prints all of the dependencies of a module to stdout, and the output includes the module's remote origin as well as its cached path on your local storage device.
It also has a (currently unstable) argument --json which will print the dependencies in JSON format (which is easier to parse programmatically if that's your goal).
You can create a subprocess in your program that uses the above command and argument, then parse its output in order to programmatically determine a module's cached location. (Also note that the subprocess API is changing and the currently unstable APIs Deno.spawn and Deno.spawnChild will replace the current one.)
Here's some example output from a module with fewer dependencies than the one in your question:
% deno info https://deno.land/std#0.154.0/testing/asserts.ts
local: /Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9
type: TypeScript
dependencies: 3 unique (total 46.65KB)
https://deno.land/std#0.154.0/testing/asserts.ts (23.22KB)
β”œβ”€β”€ https://deno.land/std#0.154.0/fmt/colors.ts (11.62KB)
β”œβ”€β”¬ https://deno.land/std#0.154.0/testing/_diff.ts (11.11KB)
β”‚ └── https://deno.land/std#0.154.0/fmt/colors.ts *
└── https://deno.land/std#0.154.0/testing/_format.ts (705B)
Output when using the --json argument:
% deno info --json https://deno.land/std#0.154.0/testing/asserts.ts
{
"roots": [
"https://deno.land/std#0.154.0/testing/asserts.ts"
],
"modules": [
{
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/02d8068ecd90393c6bf5c8f69b02882b789681b5c638c210545b2d71e604b585",
"emit": null,
"map": null,
"size": 11904,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts"
},
{
"dependencies": [
{
"specifier": "../fmt/colors.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts",
"span": {
"start": {
"line": 11,
"character": 7
},
"end": {
"line": 11,
"character": 25
}
}
}
}
],
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/62cb97c1d18d022406d28b201c22805c58600e9a6d837b0fc4b71621ed21e30d",
"emit": null,
"map": null,
"size": 11380,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/_diff.ts"
},
{
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/3f50b09108fe404c8274e994b417a0802863842e740c1d7ca43c119c0ee0f14b",
"emit": null,
"map": null,
"size": 705,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/_format.ts"
},
{
"dependencies": [
{
"specifier": "../fmt/colors.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/fmt/colors.ts",
"span": {
"start": {
"line": 10,
"character": 32
},
"end": {
"line": 10,
"character": 50
}
}
}
},
{
"specifier": "./_diff.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/testing/_diff.ts",
"span": {
"start": {
"line": 11,
"character": 44
},
"end": {
"line": 11,
"character": 56
}
}
}
},
{
"specifier": "./_format.ts",
"code": {
"specifier": "https://deno.land/std#0.154.0/testing/_format.ts",
"span": {
"start": {
"line": 12,
"character": 23
},
"end": {
"line": 12,
"character": 37
}
}
}
}
],
"kind": "esm",
"local": "/Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9",
"emit": null,
"map": null,
"size": 23776,
"mediaType": "TypeScript",
"specifier": "https://deno.land/std#0.154.0/testing/asserts.ts"
}
],
"redirects": {}
}
However, if your ultimate goal is to cache modules (that come from a remote origin) to your local storage device and import them from that location rather than use Deno's built-in cache, I recommend using the built-in tool for that: deno vendor. From its manual page:
deno vendor <specifiers>... will download all remote dependencies of the specified modules into a local vendor folder.
Update: Here's an example script demonstrating the method I described above:
so-73596066.ts:
/// <reference lib="deno.unstable" />
import { assertExists } from "https://deno.land/std#0.154.0/testing/asserts.ts";
/*
`deno info --json` is unstable, and I didn't find any mention of schema for its
output in the docs, but here's a (conservative) partial type for the bits
that are relevant to this example, derived from looking at just a couple
of outputs from Deno v1.25.1:
*/
type ModuleInfo =
& Record<"kind" | "local" | "mediaType" | "specifier", string>
& Record<"emit" | "map", string | null>
& {
dependencies?: unknown[];
size: number;
};
type DependencyInspectorResult = {
modules: ModuleInfo[];
roots: string[];
};
/**
* Creates a formatted error message and allows for improved error handling by
* discriminating error instances
*/
class ProcessError extends Error {
override name = "ProcessError";
constructor(status: Deno.ChildStatus, stdErr?: string) {
let msg = `The process exited with status code ${status.code}`;
if (stdErr) msg += `. stderr:\n${stdErr}`;
super(msg);
}
}
/**
* Parses output from `deno info --json`. The resulting command will look like:
* `deno info --json [...denoInfoArgs] specifier`
* #param specifier local/remote path/URL
* #param denoInfoArgs optional, additional arguments to be used with `deno info --json`
*/
async function getCachedModuleInfo(
specifier: string | URL,
denoInfoArgs?: string[],
): Promise<ModuleInfo> {
const decoder = new TextDecoder();
const specifierStr = String(specifier);
const args = ["info", "--json"];
if (denoInfoArgs?.length) args.push(...denoInfoArgs);
args.push(specifierStr);
const { stderr, stdout, ...status } = await Deno.spawn("deno", { args });
if (!status.success) {
const stdErr = decoder.decode(stderr).trim();
throw new ProcessError(status, stdErr);
}
const result = JSON.parse(
decoder.decode(stdout),
) as DependencyInspectorResult;
const moduleInfo = result.modules.find((info) =>
info.specifier === specifierStr
);
assertExists(moduleInfo, "Module not found in output");
return moduleInfo;
}
/**
* `console.log` truncates long strings and deep object properties by default.
* This overrides that behavior.
*/
function print(value: unknown): void {
const inspectOpts: Deno.InspectOptions = {
colors: true,
depth: Infinity,
strAbbreviateSize: Infinity,
};
const formattedOutput = Deno.inspect(value, inspectOpts);
console.log(formattedOutput);
}
async function main() {
const moduleInfo = await getCachedModuleInfo(
"https://deno.land/std#0.154.0/testing/asserts.ts",
);
const { local, specifier } = moduleInfo;
print({ specifier, local });
}
if (import.meta.main) main();
% deno --version
deno 1.25.1 (release, x86_64-apple-darwin)
v8 10.6.194.5
typescript 4.7.4
% deno run --allow-run=deno --unstable so-73596066.ts
{
specifier: "https://deno.land/std#0.154.0/testing/asserts.ts",
local: "/Users/deno/.deno/deps/https/deno.land/ce1734220fb8c205d2de1b52a59b20401c59d0707abcc9765dbeb60c25483df9"
}

how to filter only one data with a specific slug in sanity.io?

Data:
[
{
"name": "Gates of Olympus",
"slug": {
"_type": "slug",
"current": "gates-of-olympus"
}
},
{
"name": "Floating Dragon",
"slug": {
"_type": "slug",
"current": "floating-dragon"
}
},
{
"name": "Buffalo King Megaways",
"slug": {
"_type": "slug",
"current": "buffalo-king-megaways"
}
},
{
"name": "Fruit Party",
"slug": {
"_type": "slug",
"current": "fruit-party"
}
}
]
How do I query only objects with slug gates-of-olympus ?
Code:
export const getServerSideProps = async ({params}:any) => {
const query = `*[_type=="game"]{
name,
slug,
}`;
const games = await sanityClient.fetch(query);
return {
props: {
games,
},
};
};
slug is obtained through context (params.game).
I also tried,
*[_type=="game" && slug.current == ${params.game}] but still returns all data.
Wrap ${params.game} with the quotes. Like this "${params.game}". It will work
You get back all the data but the first one or first item in that array of data is the one your searching for so at the end of your query put [0] at the end to get the first value you should be solid eg *[_type=="game" && slug.current == '${params.game}'][0]
Ref
go to this video which is taught by js mastery skip to 1:21:27 he starts explaining how to get the current slug/product https://www.youtube.com/watch?v=4mOkFXyxfsU&t=5153s

Spring Cloud Contract generates Pacts with empty body when the body is a list

I'm trying to generate pacts from spring cloud contracts as shown at the documentation. It works just find when the response body root is a json, however when I'm trying to generate a pact that return an array of jsons it generates an empty body. IΒ΄ve tried using groovy dsl with String format """[{}...]""" and using DslProperty [value()...]. Here are my contracts:
With string format
Contract.make {
description "should return a list of dummy object with dummy value. Generates pact with empty json"
request {
method GET()
url("/dummy")
}
response {
body("""[{"value": "Hi! I'm a dummy object ;)"}]""")
headers {
contentType applicationJson()
}
status 200
}}
With DslProperty
Contract.make {
description "should return a list of dummy object with dummy value. Generates pact with empty body list"
request {
method GET()
url("/dummy")
}
response {
body([value(value: "Hi! I'm a dummy object ;)")])
headers {
contentType applicationJson()
}
status 200
}}
And that's the file generate at target/pacts
{
"provider": {
"name": "Provider"
},
"consumer": {
"name": "Consumer"
},
"interactions": [
{
"description": "should return a list of dummy object with dummy value. Generates pact with empty body list",
"request": {
"method": "GET",
"path": "/dummy"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
],
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
}
}
}
},
{
"description": "should return a list of dummy object with dummy value. Generates pact with empty json",
"request": {
"method": "GET",
"path": "/dummy"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.23"
}
}}
I'm using the following versions
<spring-cloud.version>Hoxton.BUILD-SNAPSHOT</spring-cloud.version>
<spring-cloud-contract.version>2.0.1.RELEASE</spring-cloud-contract.version>
<pact-jvm-provider-maven.version>3.5.23</pact-jvm-provider-maven.version>
and that's my plugin configuration
<!-- SCC to pact see https://cloud.spring.io/spring-cloud-contract/reference/html/howto.html#how-to-generate-pact-from-scc-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>convert-dsl-to-pact</id>
<phase>process-test-classes</phase>
<configuration>
<classpathScope>test</classpathScope>
<mainClass>
org.springframework.cloud.contract.verifier.util.ToFileContractsTransformer
</mainClass>
<arguments>
<argument>
org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
</argument>
<argument>${project.basedir}/target/pacts</argument>
<argument>
${project.basedir}/src/test/resources/contracts
</argument>
</arguments>
</configuration>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
While debugging throgh the plugin I've seen that what is happening respectively is:
- When declaring body as β€œβ€β€ [{...}] β€œβ€β€ Pact converter assumes that the body is an String instance so it goes through traverse method at org.springframework.cloud.contract.verifier.spec.pact.BodyConverter. And since it starts with [ it is not parsed.
org.springframework.cloud.contract.verifier.spec.pact.BodyConverter
private static DslPart traverse(Object value, DslPart parent, Closure dslPropertyValueExtractor) {
...
if (v instanceof String) {
v = v.trim()
if (v.startsWith("{") && v.endsWith("}")) {
try {
v = jsonSlurper.parseText(v as String)
}
catch (JsonException ex) { /*it wasn't a JSON string after all...*/
}
}
}
...
On the other hand, when going through the plugin code using the DslProperty I have an object like [DslProperty{clientValue=DslProperty}]. The first DslProperty is being extracted but since the content is another DslProperty and there’s no recursive extraction I end up with an empty body because v isn’t an instance of the Gstring, String, Number, Map, Collection. So I get an empty body again.
org.springframework.cloud.contract.verifier.spec.pact.BodyConverter
private static void processCollection(Collection values, PactDslJsonArray jsonArray, Closure dslPropertyValueExtractor) {
values.forEach({
Object v = it
if (v instanceof DslProperty) {
v = dslPropertyValueExtractor(v)
}
if (v instanceof GString) {
v = ContentUtils.extractValue(v, dslPropertyValueExtractor)
}
if (v == null) {
jsonArray.nullValue()
}
else if (v instanceof String) {
jsonArray.string(v)
}
else if (v instanceof Number) {
jsonArray.number(v)
}
else if (v instanceof Map) {
PactDslJsonBody current = jsonArray.object()
traverse(v, current, dslPropertyValueExtractor)
current.closeObject()
}
else if (v instanceof Collection) {
PactDslJsonArray current = jsonArray.array()
traverse(v, current, dslPropertyValueExtractor)
current.closeArray()
}
})
}
I've published an example at https://github.com/brjt23/contract-to-pact/tree/master in case more info about how I did build the project is required.
Is there something I'm doing wrong on defining my groovy contract files? I guess I missunderstood something about how the response body should be defined.
You need to create an array of groovy objects in your body like this:
body([
[value: "Object1"],
[value: "Object2"]
])
This way spring cloud contracts will generate the correct code needed for your contracts.

Task timed out after 3.00 seconds - Lambda application with nodeJS

I am trying to put a hard-coded data item to DynamoDB. I am using AWS SDK object to perform this update. And all the debug "Console.log" in the below code is getting printed but eventually it prints Task timed out after 3.00 seconds
With no update to the DynamoDB
function updatedb(intent, session, callback) {
let country;
const repromptText = null;
const sessionAttributes = {};
let shouldEndSession = false;
console.log("In the function");
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient({ region: 'eu-west-1' });
var params = {
TableName: "Location",
Item: {
"LocationID": { "S": "11" },
"Country": { "S": "10" },
"Description": { "S": "10" },
"Name": { "S": "10" }
}
};
console.log("Param loaded & executing the DocClient Put");
docClient.put(params, function (err, data) {
if (err) {
speechOutput = 'Update failed';
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
speechOutput = 'Update successful';
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
}
});
}
The following items are already checked
1) There is a table named "Location" in DynamoDB
2) Both DynamoDB and this lambda function are in ue-west-1 (Ireland)
3) The role assigned for this Lambda function can do all operation on this table. See the policy details below
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1510603004000",
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"arn:aws:dynamodb:eu-west-1:752546663632:table/Location"
]
}
]
}
How does my Lambda function locate the table "location" just with the region?- the code does not appear to have end-point, etc.? - just developed based on a tutorial.
Is that what I am missing?
Please can you help?
I had a similar issue, try putting require statements in the beginning of your function.
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient({ region: 'eu-west-1' });
I believe that AWS locates the table based on your identity, in combination with the region and the table name.
I was able to successfully post to a table using this code:
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB({region: 'us-west-2'});
var params = {
TableName: "my-table",
Item: {
"LocationID": { S: "11" },
"Country": { S: "10" },
"Description": { S: "10" },
"Name": { S: "10" }
}
};
dynamoDB.putItem(params, (err, data) => {
if (err){
console.error(err.stack);
} else {
console.log(data);
}
});
If you can in fact post to the table from the CLI, then there is still at least one remaining issue: it appears that you are using the DocumentClient class incorrectly. It looks like you're mixing up the syntax for DynamoDB.putItem with the syntax for DynamoDB.DocumentClient.put.
If you notice, my code uses the DynamoDB class directly-- based on what you're doing, I see no reason why you couldn't do the same. Otherwise, you should change your Item object:
var params = {
TableName: "my-table",
Item: {
"LocationID": "11",
"Country": "10",
"Description": "10",
"Name": "10"
}
};
My guess is your code is currently erroring out because you are trying to insert Maps where you want to insert Strings. If you have Cloudwatch configured you could check the logs.
Finally, I don't see you using callback in your code. If your intention is to respond to a client calling the lambda you should do that. Depending on your NodeJS version, the lambda can simply time out without returning a useful response.

Normalize data model in RxJava without for loops and temporary arrays

Given the following response from a api request I would like to normalize the datamodel into a simpler one using rxjava without using any loops or temporary arrays to store information.
Given: List<MyItem>
===================
MyItem
String category_name
List<Switch> switches;
Switch
String id
boolean active
Response:
[{
"category_name": "Sport",
"switches": [{
"id": "sport_01",
"active": true
}, {
"id": "sport_02",
"active": false
}, {
"id": "sport_03",
"active": true
}]
}, {
"category_name": "Economy",
"switches": [{
"id": "economy_01",
"active": true
}, {
"id": "economy_02",
"active": true
}]
}]
Expected Normalised: List<MyViewModel>
===========================
MyViewModel
String categoryName
String switchId
boolean switchActiveState
Example
[{
"category_name": "Sport",
"id": "sport_01",
"active": true
}, {
"category_name": "Sport",
"id": "sport_02",
"active": false
}, ...
]
My first approach was the following
Observable<MyItem> mMyItemObservable = mApi.getReponse()
.map(mToMyItemList)
.flatMapIterable(mToMyItem)
.share();
Observable<Switch> switchObservable = mMyItemObservable.flatMap(new Func1<MyItem, Observable<List<Switch>>>() {
#Override
public Observable<List<Switch>> call(final MyItem item) {
return Observable.defer(new Func0<Observable<List<Switch>>>() {
#Override public Observable<List<Switch>> call() {
return Observable.just(item.switches);
}
});
}
}).flatMapIterable(new Func1<List<Switch>, Iterable<Switch>>() {
#Override
public Iterable<Switch> call(List<Switch> switches) {
return switches;
}
});
I have tried to use the following
Observale.zip(mMyItemObservable, switchObservable, Func...)
Observale.combineLatest(mMyItemObservable, switchObservable, Func...)
in order to produce the endResult List but with no success because most probably the length of the 2 observables were different.
ie. mMyItemObservable with length 2 (items) and the switchObservable with length 5 items.
Any ideas of alternative ways to group these 2 observables together to achieve the end result?
You need the 2-element version of flatMap:
mMyItemObservable =
mApi.getReponse()
.map(mToMyItemList)
.flatMapIterable(mToMyItem)
.flatMap(item -> Observable.from(item.switches),
(item, switch) -> new MyViewModel(item, switch)
);
Also please tell whoever gave you this homework to a) get into the 21st century and drop all the m- prefixes b) start using Java 8 features on Android (makes life with RxJava much easier.

Resources