Consume Spring Flux by Retrofit Observable/Flowable - retrofit2

Is it possible to consume Flux endpoint as Observable or Flowable in Retrofit?
What I am aiming to achieve is to emit items from endpoint to the consumer.
Spring boot WebFlux endpoint
#RestController
#RequestMapping("user")
class UserController {
private val repo = listOf(
UserDoc().apply {
id = 1
name = "test1"
createdAt = LocalDateTime.now()
},
UserDoc().apply {
id = 2
name = "test2"
createdAt = LocalDateTime.now()
},
UserDoc().apply {
id = 3
name = "test3"
createdAt = LocalDateTime.now()
}
)
#GetMapping
fun findAll(): Flux<UserDoc> = Flux.just(*repo.toTypedArray())
}
Sample response
[
{
"id": 1,
"name": "test1",
"createdAt": "2022-10-04T15:25:34.540953"
},
{
"id": 2,
"name": "test2",
"createdAt": "2022-10-04T15:25:34.540976"
},
{
"id": 3,
"name": "test3",
"createdAt": "2022-10-04T15:25:34.54098"
}
]
Retrofit client
fun main() {
val userApi = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://localhost:2020/")
.client(OkHttpClient())
.build()
.create(UserApi::class.java)
val compositeDisposable = CompositeDisposable()
compositeDisposable.add(
userApi.findAll()
.subscribeOn(Schedulers.io())
.subscribe {user: User ->
println(user)
}
)
Thread.currentThread().join()
}
interface UserApi {
#GET("user")
fun findAll(): Flowable<User>
}
data class User(
val id: Long,
val name: String
)
The current implementation return this error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
If I change the Flowable<User> to Flowable<List<User>> it's works fine.
Is it possible to subscribe the list user by user? or create WebSocket and create my custom Observable?

Related

object to Tuple with list and dictionary c#

{
"data": [
{
"id": 10,
"title": "Administration",
"active": true,
"type": {
"id": 2,
"name": "Manager"
}
},
{
"id": 207,
"title": "MCO - Exact Match 1",
"active": true,
"type": {
"id": 128,
"name": "Group"
}
},
{
"id": 1201,
"title": "Regression",
"active": false,
"type": {
"id": 2,
"name": "Manager"
}
}
]
}
i am trying to create a tuple in the below format using linq. not sure how to start with group/aggregate. Any help is appreciated. I went over few threads and could not able to find something similar to this.
var tuple = new List<Tuple<int, List<Dictionary<int,bool>>>();
2 10, true
1201, false
128 207, true
Here is a full working code:
var o = new {
data = new [] {
new {
id = 10,
title = "Administration",
active = true,
type = new {
id = 2,
name = "Manager"
}
},
new {
id = 207,
title = "MCO - Exact Match 1",
active = true,
type = new {
id = 128,
name = "Group"
}
},
new {
id = 1201,
title = "Regression",
active = false,
type = new {
id = 2,
name = "Manager"
}
}
}
};
var result = o.data.GroupBy(
item => item.type.id, // the group key
item => new Dictionary<int, bool>() {{ item.id, item.active }}, // the transformed elements in the group
(id, items) => new Tuple<int, List<Dictionary<int, bool>>>(id, items.ToList()) // transformation of grouping result to the final desired format
).ToList();
// check correctness
foreach (var entry in result) {
Console.Write(entry.Item1);
foreach (var dict in entry.Item2) {
foreach (var kvp in dict)
Console.WriteLine("\t\t" + kvp.Key + "\t" + kvp.Value);
}
}
And this is how it works:
o is the data model, represented using anonymous types. You can obviously use a strongly typed model here, if you already have it;
on o we apply the four-argument version of GroupBy, described in detail in the official docs from Microsoft. Basically:
the first lambda expression selects the group key;
the second lambda defines the elements that are part of each group;
the third lambda transforms each (group key, enumeration of group elements) into the Tuple<int, List<Dictionary<int, bool>>> format;
at the end we call ToList() to compute the result and store it as a list of tuples.
the last part prints the result (did not spend much time prettifying it, but it does its job validating the code).

Convert Json to Poco Collection/ How to code a For Each?

I'm fairly new to Azure Functions.
I've created a C# WebHook / Azure Function (I guess that's the right thing) to take my json content and convert it into a collection of simple poco/dto objects.
public static class GenericWebHookCSharp
{
[FunctionName("GenericWebHookCsharpOne")]
public static async Task<HttpResponseMessage /* object */> Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
try
{
log.Info(string.Format("C# GenericWebHookCsharpOne about to process a request. ('{0}')", DateTime.Now.ToLongTimeString()));
//IUnityContainer container = new UnityContainer();
//container.RegisterType<IJsonToPersonRequestWrapperConverter, JsonToPersonRequestWrapperConverter>();
//IJsonToPersonRequestWrapperConverter jsonConvtr = container.Resolve<IJsonToPersonRequestWrapperConverter>();
//ICollection<Person> emps = await jsonConvtr.ConvertHttpRequestMessageToPersonCollection(req);
/* above commented code is my "real" code where I take the INPUT request-body-as-json and convert it into a ICollection of Person(s) */
/* below code, I just fake-creating some persons */
string jsonContent = await req.Content.ReadAsStringAsync();
ICollection<Person> persons = new List<Person>();
for(int i = 0; i< 10; i++)
{
persons.Add(new Person() { PersonUuid = Guid.NewGuid(), LastName = "LN" + i.ToString(), FirstName = "FN" + i.ToString(), BirthDate = DateTimeOffset.Now.AddYears(-1 * (20 + i))});
}
string serializedJson = Newtonsoft.Json.JsonConvert.SerializeObject(persons);
log.Info(string.Format("C# GenericWebHookCsharpOne finished a request. ('{0}')", DateTime.Now.ToLongTimeString()));
return req.CreateResponse(HttpStatusCode.OK , serializedJson);
}
catch (Exception ex)
{
string errorMsg = ex.Message;// ExceptionHelper.GenerateFullFlatMessage(ex);
log.Error(errorMsg);
return req.CreateResponse(HttpStatusCode.BadRequest, errorMsg);
}
}
}
In another .cs file
public class Person
{
public Guid PersonUuid { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
If I debug it in Visual Studio, it works fine.
So I added this as a step on my Logic App as seen below
So I want to add a new step that is a "for each" step. Here is what I get now: (image below). All I see is "body" from the initial trigger and the "convert" webhook function (that I have above)........
How do I get the "persons" (so I can do a for-each-person) collection to show up and be available for the next step on the Logic App?
EDIT/APPEND:
The end game is to push a service-bus-message for "each" of my Person(s).
As requested, here is the "person json".....
[{
"PersonUuid": "7ec8cc4d-831c-4c89-8516-47424ee2658d",
"LastName": "LN0",
"FirstName": "FN0",
"BirthDate": "1997-08-17T09:46:16.9839382-04:00"
},
{
"PersonUuid": "275264bc-5a86-476d-a189-512afa1e3ce4",
"LastName": "LN1",
"FirstName": "FN1",
"BirthDate": "1996-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "e522b827-2d2e-465d-a30a-c4b619d2e8e4",
"LastName": "LN2",
"FirstName": "FN2",
"BirthDate": "1995-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "f16bce36-3491-4519-bc82-580939f61b2e",
"LastName": "LN3",
"FirstName": "FN3",
"BirthDate": "1994-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "42456057-39ef-45aa-bd7c-ad6a8fa74fd1",
"LastName": "LN4",
"FirstName": "FN4",
"BirthDate": "1993-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "14088a6e-3c44-4cb0-927d-19f5eda279c4",
"LastName": "LN5",
"FirstName": "FN5",
"BirthDate": "1992-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "332a5cde-3cd1-467a-9dfc-2b187d6ae32e",
"LastName": "LN6",
"FirstName": "FN6",
"BirthDate": "1991-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "6debe134-19e6-4b16-a91d-05ded511eff6",
"LastName": "LN7",
"FirstName": "FN7",
"BirthDate": "1990-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "e61ef8a1-09d3-4c5b-b948-df8e0858cd29",
"LastName": "LN8",
"FirstName": "FN8",
"BirthDate": "1989-08-17T09:46:16.9844385-04:00"
},
{
"PersonUuid": "e9b27632-d3a4-4fe8-8745-04edfa8854f7",
"LastName": "LN9",
"FirstName": "FN9",
"BirthDate": "1988-08-17T09:46:16.9844385-04:00"
}]
Ok.
I gotta get this written down before I forget. Wow, what a ride.
First the C# code on the WebHook. Note the "anonymousPersonWrapper" code.
public static class GenericWebHookCSharp
{
[FunctionName("GenericWebHookCsharpOne")]
public static async Task<HttpResponseMessage /* object */> Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
try
{
log.Info(string.Format("C# GenericWebHookCsharpOne about to process a request. ('{0}')", DateTime.Now.ToLongTimeString()));
///////* below code, I just fake-creating some persons */
string jsonContent = await req.Content.ReadAsStringAsync();
ICollection<Person> persons = new List<Person>();
for (int i = 0; i < 3; i++)
{
persons.Add(new Person() { PersonUuid = Guid.NewGuid(), LastName = "LN" + i.ToString(), FirstName = "FN" + i.ToString(), BirthDate = DateTimeOffset.Now.AddYears(-1 * (20 + i)) });
}
/* the below is the "trick" to allow the for-each to work in the Logic-App. at least in my experience */
var anonymousPersonWrapper = new
{
personWrapper = persons
};
string personWrapperJsonString = JsonConvert.SerializeObject(anonymousPersonWrapper);
log.Info(string.Format("C# GenericWebHookCsharpOne finished a request. ('{0}')", DateTime.Now.ToLongTimeString()));
HttpResponseMessage returnReq = req.CreateResponse(HttpStatusCode.OK , personWrapperJsonString );
return returnReq;
}
catch (Exception ex)
{
string errorMsg = ex.Message;
log.Error(errorMsg);
return req.CreateResponse(HttpStatusCode.BadRequest, errorMsg);
}
}
}
But putting a breakpoint on "return returnReq;", I was able to see that personWrapperJsonString contained the below Json:
{
"personWrapper": [{
"PersonUuid": "31fb318d-a9bf-4c2f-ad16-0810ddd73746",
"LastName": "LN0",
"FirstName": "FN0",
"BirthDate": "1997-08-17T15:10:08.9633612-04:00"
},
{
"PersonUuid": "73fdacc7-e1e8-48ff-b161-1bd8b5f4aec1",
"LastName": "LN1",
"FirstName": "FN1",
"BirthDate": "1996-08-17T15:10:08.9633612-04:00"
},
{
"PersonUuid": "d18b4324-2d3e-41ca-9525-fe769af89e9c",
"LastName": "LN2",
"FirstName": "FN2",
"BirthDate": "1995-08-17T15:10:08.9633612-04:00"
}]
}
Ok.
Then I added a "Parse Json" action (below image)
Then I setup the Parse-Json. Below.
The above parse-json setup is not complete.
Click on the button "Use sample payload to generate schema" and that will pop a new window. Paste in your "personWrapper" json from earlier. As seen in the below image.
The above will of course create the json-schema that you need (that is for-each friendly). As seen below.
Now we're so close.
Add a For-Each (using the "More" button when you add a new step) (as seen below)
Now we setup the for-each. Looked what showed up! The "personWrapper" (below image)
For grins, I made the sessionId be the PersonUuid value (just to show that I can get hold of one of the scalar properties of the object. (image below).
And now the json as the Content of the Service Bus message. (below image)
I then published the Azure-Functions and deployed the Logic-App, and sent a request to the trigger.
Back to azure portal. The PersonUuid showed up as the SessionId! (image below)
And a quick check in Service Bus Explorer to "peek" the contents of the message (image below)
Ok, a few breadcrumbs:
I got a hint from here about putting the collection side a "wrapper".
Json.NET validate JSON array against Schema
A few errors I got along the way
"Invalid type. Expected Object but got Array."
UnsupportedMediaType "The WebHook request must contain an entity body formatted as JSON."
"this output is an array" "a foreach cannot be nested inside of another foreach"
'Json' expects its parameter to be a string or an XML.The provided value is of type 'Array.
As Steven Van Eycken mentioned that we could parse string to array with json fucntion in the logic application. In your case we could parse the string to array in the Logic app or return jarry directly from Azure function . We can choose one of the following ways to do that . I also test it on my side, it works correctly.
In the logic App
json(body('Your action name'))
Or
Return Jarry directly in the Azure function
var jarry =JArray.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(persons));
log.Info(string.Format("C# GenericWebHookCsharpOne finished a request. ('{0}')", DateTime.Now.ToLongTimeString()));
return req.CreateResponse(HttpStatusCode.OK, jarry);

Conditional Iteration on a parsed json object using each in groovy

I'm trying to create an XML based on the data from a .json. So, my .json file looks something like:
{
"fruit1":
{
"name": "apple",
"quantity": "three",
"taste": "good",
"color": { "walmart": "{{red}}","tj": "{{green}}" }
},
"fruit2":
{
"name": "banana",
"quantity": "five",
"taste": "okay",
"color": { "walmart": "{{gmo}}","tj": "{{organic}}" }
}
}
I can create the XML just fine with the below code, from the above json
import groovy.xml.*
import groovy.json.JsonSlurper
def GenerateXML() {
def jsonSlurper = new JsonSlurper();
def fileReader = new BufferedReader(
new FileReader("/home/workspace/sample.json"))
def parsedData = jsonSlurper.parse(fileReader)
def writer = new FileWriter("sample.XML")
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
writer << builder.bind {
mkp.xmlDeclaration()
"friuts"(version:'$number', application: "FunApp"){
delegate.deployables {
parsedData.each { index, obj ->
"fruit"(name:obj.name, quantity:obj.quantity) {
delegate.taste(obj.taste)
delegate.color {
obj.color.each { name, value ->
it.entry(key:name, value)
}
}
}
}
}
}
}
}
I want to extend this code, such that it looks for particular keys. And if they are present, the loop is performed for those maps as well and as such extends the resulting file.
So, if i have the JSON as like so:
{"fruit1":
{
"name": "apple",
"quantity": "three",
"taste": "good",
"color": { "walmart": "{{red}}","tj": "{{green}}" }
},
"fruit2":
{
"name": "banana",
"quantity": "five",
"taste": "okay",
"color": { "walmart": "{{gmo}}","tj": "{{organic}}" }
},
"chip1":
{
"name": "lays",
"quantity": "one",
"type": "baked"
},
"chip2":
{
"name": "somename",
"quantity": "one",
"type": "fried"
}
}
I want to add an IF, so that it check if any key(s) like 'chip*' is there. And if yes, perform another iteration. If not just skip that section of logic, and not throw any err. like this
import groovy.xml.*
import groovy.json.JsonSlurper
def GenerateXML() {
def jsonSlurper = new JsonSlurper();
def fileReader = new BufferedReader(
new FileReader("/home/okram/workspace/objectsRepo/sample.json"))
def parsedData = jsonSlurper.parse(fileReader)
def writer = new FileWriter("sample.XML")
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
writer << builder.bind {
mkp.xmlDeclaration()
"fruits"(version:'$number', application: "FunApp"){
deployables {
parsedData.each { index, obj ->
"fruit"(name:obj.name, quantity:obj.quantity) {
taste(obj.taste)
color {
obj.color.each { name, value ->
it.entry(key:name, value)
}
}
}
}
}
}
if (parsedData.containsKey('chip*')){
//perform the iteration of the chip* maps
//to access the corresponding values
//below code fails, but that is the intent
parsedData.<onlyTheOnesPassing>.each { index1, obj1 ->
"Chips"(name:obj1.name, quantity:obj1.quantity) {
type(obj1.type)
}
}
}
}
}
I found the same dificult, but on Javascript language, if the logic help you, here what I made:
There are two ways:
You can use the library Lodash on the "get" here: Lodash get or the another one "has": Lodash has.
With they you can put the object and the path and check if there is one without getting any error.
Examples:
_.has(object, 'chip1.name');
// => false
_.has(object, 'fruit1');
// => true
Or you can put the code of the methods here:
// Recursively checks the nested properties of an object and returns the
// object property in case it exists.
static get(obj, key) {
return key.split(".").reduce(function (o, x) {
return (typeof o == "undefined" || o === null) ? o : o[x];
}, obj);
}
// Recursively checks the nested properties of an object and returns
//true in case it exists.
static has(obj, key) {
return key.split(".").every(function (x) {
if (typeof obj != "object" || obj === null || !x in obj)
return false;
obj = obj[x];
return true;
});
}
I hope it helps! :)

groovy object with trait to json conversion

I am trying to convert object to JSON. Object has a trait which supposed to convert object. But I get weird json result.
import groovy.json.*
trait JsonPackageTrait {
def toJson() {
JsonOutput.prettyPrint(
JsonOutput.toJson(this)
)
}
}
class Item {
def id, from, to, weight
}
def item = new Item()
item.with {
id = 1234512354
from = 'London'
to = 'Liverpool'
weight = 15d.lbs()
}
item = item.withTraits JsonPackageTrait
println item.toJson()
JSON result
{
"from": "London",
"id": 1234512354,
"to": "Liverpool",
"proxyTarget": {
"from": "London",
"id": 1234512354,
"to": "Liverpool",
"weight": 33.069
},
"weight": 33.069
}
So it seems I cannot do it like this?
Well, whatever. As using withTraits leads to creating proxy of the original object I resolved like this for my current implementation
trait JsonPackageTrait {
def toJson() {
JsonOutput.prettyPrint(
JsonOutput.toJson(this.$delegate)
)
}
}

ObjectGraphBuilder from and to a file

How can I make ObjectGraphBuilder to build my class instance from an string? I mean if I have
String myString = """invoices{
invoice(date: new Date(106,1,2)){
item(count:5){
product(name:'ULC', dollar:1499)
}
item(count:1){
product(name:'Visual Editor', dollar:499)
}
}
invoice(date: new Date(106,1,2)){
item(count:4) {
product(name:'Visual Editor', dollar:499)
}
}
"""
how can turn this string (myString) into an instance of the invoice class (I assume I have to use ObjectGraphBuilder but how?)
Given an instance of the class invoice ( with all of its nested properties), how can I turn that instance into an string like myString?
I also want to be able serialize and deserialize from a text file too but I assume it is the same as the string.
You can work with GroovyShell to evaluate the string and delegate the methods called in the script to an ObjectGraphBuilder. I repeated the "invoices" method. If this is unacceptable, take a look at Going to Mars with Domain-Specific Languages, by Guillaume Laforge, where he teaches how to customize the compiler.
I also created an Invoices class, because of the way ObjectGraphBuilder works. If this will be dynamic for you, take a look at its resolvers.
import groovy.transform.ToString as TS
#TS class Invoices { List<Invoice> invoices=[] }
#TS class Invoice { List<Item> items=[]; Date date }
#TS class Item { Integer count; Product product }
#TS class Product { String name; Integer dollar; Vendor vendor }
#TS class Vendor { Integer id }
String myString = """
invoices {
invoice(date: new Date(106,1,2)){
item(count:5){
product(name:'ULC', dollar:1499)
}
item(count:1){
product(name:'Visual Editor', dollar:499)
}
}
invoice(date: new Date(106,1,2)){
item(count:4) {
product(name:'Visual Editor', dollar:499)
}
}
}
"""
invoicesParser = { Closure c ->
new ObjectGraphBuilder().invoices c
}
binding = new Binding( [invoices: invoicesParser] )
invoices = new GroovyShell(binding).evaluate myString
assert invoices.invoices.size() == 2
Update: as for your second question, i'm not aware, and neither could found, any way back to the object graph builder representation. You can roll your own, but i think you will be better if you try something like json. Does your use case permit you to do so?
use( groovy.json.JsonOutput ) {
assert invoices.toJson().prettyPrint() == """{
"invoices": [
{
"date": "2006-02-02T02:00:00+0000",
"items": [
{
"product": {
"vendor": null,
"dollar": 1499,
"name": "ULC"
},
"count": 5
},
{
"product": {
"vendor": null,
"dollar": 499,
"name": "Visual Editor"
},
"count": 1
}
]
},
{
"date": "2006-02-02T02:00:00+0000",
"items": [
{
"product": {
"vendor": null,
"dollar": 499,
"name": "Visual Editor"
},
"count": 4
}
]
}
]
}"""
}

Resources