So I have created a simple chat bot, and I'm having trouble with one of it's commands, /resetall. It is supposed to change every users values to the values of the user "default". However, it seems that default is causing all values to change by 2.
"data": {
"user": {
"bob": {
"admin": "true",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "2"
},
"default": {
"admin": "true",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "2"
},
"me": {
"admin": "true",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "2"
},
"total": {
"admin": "true",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "2"
}
},
"chat": {
"commandSender": "me",
"lastImage": "",
"lastMessage": "/pong",
"lastSender": "me",
"lastTimestamp": "11:59",
"wasCommand": "true"
}
}
and my go code:
// each incoming message
type Message struct {
Message string
From string
Chat string
Timestamp string
IsCommand bool
}
//adds one to a string
func addOne(s string) string {
i, _ := strconv.Atoi(s)
return strconv.Itoa(i + 1)
}
//counts messages sent
func messageCounter(data map[string]Chat, event *Message) map[string]Chat {
//counts messages sent by user
data[event.Chat].Data.User[event.From]["sentMessages"] = addOne(data[event.Chat].Data.User[event.From]["sentMessages"])
data[event.Chat].Data.User["total"]["sentMessages"] = addOne(data[event.Chat].Data.User["total"]["sentMessages"])
return data
}
//sets variables for future use / other functions
func eventRecorder(data map[string]Chat, event *Message) map[string]Chat {
if !event.IsCommand {
data[event.Chat].Data.Chat["lastMessage"] = event.Message
data[event.Chat].Data.Chat["lastSender"] = event.From
data[event.Chat].Data.Chat["lastTimestamp"] = event.Timestamp
data[event.Chat].Data.Chat["wasCommand"] = "false"
} else {
data[event.Chat].Data.Chat["wasCommand"] = "true"
data[event.Chat].Data.Chat["commandSender"] = event.From
}
return data
}
//supposed to set all users data to the default user
func resetall(event *Message, data map[string]Chat) (error, map[string]Chat) {
default_user := data[event.Chat].Data.User["default"]
if data[event.Chat].Data.User[event.From]["admin"] == "true" {
for user, _ := range data[event.Chat].Data.User {
if user != "default" {
data[event.Chat].Data.User[user] = default_user
print(user + "\n")
}
}
return nil, data
}
return errors.New("don't have permission")), data
}
func main() {
processingFuncs := []func(map[string]Chat, *Message) map[string]Chat{
messageCounter,
eventRecorder,
}
data, _ := readsettings() //reads the data from a json file
event := &Message{"/resetall", "me", "chat123", "11:59", false}
if strings.Split(event.Message, " ")[0] == "/resetall" {
event.IsCommand = true
_, data = resetall(event, data)
fmt.Println("success")
}
for _, processingFunc := range processingFuncs {
processingFunc(data, event)
}
writesettings(data) //writes the data to a json file
}
So if I set everyone's message counter to 0 and run it, it sets every single user's message counter to 2 (including default). Each time I run it, the value increases by 2. Can anyone help explain why this is happening
this line to copy the user
data[event.Chat].Data.User[user] = default_user
made a reference to it, I don't know why I didn't spot it earlier. I replaced it with this code
for k, v := range data[event.Chat].Data.User["default"] {
data[event.Chat].Data.User[user][k] = v
}
and it worked perfectly.
Related
I'm trying to construct a Go Swagger response to my existing code without changing a bunch of it and I currently have:
// DataExpressionInput - only public so we can use it embedded in dataExpression
// swagger:model
type DataExpressionInput struct {
Name string `json:"name"`
Expression string `json:"expression"`
Type expressionType `json:"type"`
Comments string `json:"comments"`
Tags []string `json:"tags"`
}
// swagger:model dataExpressionModel
type DataExpression struct {
// The main data expression information
//
// Required: true
*DataExpressionInput
// Additional metadata
*pixlUser.APIObjectItem
}
//swagger:response dataExpressionLookup
type DataExpressionLookup map[string]DataExpression
I'm trying to return a dataExpressionLookup Object via my API but when I export the swagger definition i get:
"definitions": {
"DataExpressionInput": {
"description": "DataExpressionInput - only public so we can use it embedded in dataExpression",
"x-go-package": "github.com/pixlise/core/v2/core/expression"
},
"dataExpressionModel": {
"x-go-name": "DataExpression",
"x-go-package": "github.com/pixlise/core/v2/core/expression"
}
},
"responses": {
"dataExpressionLookup": {
"description": "",
"schema": {}
},
"deleteResponse": {
"description": "",
"schema": {}
},
"genericError": {
"description": "",
"schema": {}
},
"shareResponse": {
"description": "",
"schema": {}
}
},
Is there a way to remove literally all null or empty string values from an object? We have an aggregation which creates an object with empty fields and empty objects should the value be null.
What we wish to do is remove all null properties and empty objects and recreate the object, in order to keep the data as small as possible.
e.g. in the following object, only 'test' and 'more-nested-data' should be taken into account, the rest can be removed
{
"test": "some",
"test2": {
},
"test3": {
"some-key": {
},
"some-other-key": {
"more-nested-data": true,
"more-nested-emtpy": null
}
}
}
which should become:
{
"test": "some",
"test3": {
"some-other-key": {
"more-nested-data": true
}
}
}
I tried a lot, but I think by using objectToArray that something could be done, but I have not found the solution yet. The required aggregation should need to recursively (or by defined levels) remove null properties and empty objects.
Use the $function operator available in 4.4 (Aug 2021) to do this recursively as you note. Given this input which is a slightly expanded version of that supplied in the question:
var dd = {
"test": "some",
"test2": { },
"test3": {
"some-key": { },
"some-other-key": {
"more-nested-data": true,
"more-nested-emtpy": null,
"emptyArr": [],
"notEmptyArr": [
"XXX",
null,
{"corn":"dog"},
{"bad":null},
{"other": {zip:null, empty:[], zap:"notNull"}}
]
}
}
}
db.foo.insert(dd);
then this pipeline:
db.foo.aggregate([
{$replaceRoot: {newRoot: {$function: {
body: function(obj) {
var process = function(holder, spot, value) {
var remove_it = false;
// test FIRST since [] instanceof Object is true!
if(Array.isArray(value)) {
// walk BACKWARDS due to potential splice() later
// that will change the length...
for(var jj = value.length - 1; jj >= 0; jj--) {
process(value, jj, value[jj]);
}
if(0 == value.length) {
remove_it = true;
}
} else if(value instanceof Object) {
walkObj(value);
if(0 == Object.keys(value).length) {
remove_it = true;
}
} else {
if(null == value) {
remove_it = true;
}
}
if(remove_it) {
if(Array.isArray(holder)) {
holder.splice(spot,1); // snip out the val
} else if(holder instanceof Object) {
delete holder[spot];
}
}
};
var walkObj = function(obj) {
Object.keys(obj).forEach(function(k) {
process(obj, k, obj[k]);
});
}
walkObj(obj); // entry point!
return obj;
},
args: [ "$$CURRENT" ],
lang: "js"
}}
}}
]);
produces this result:
{
"_id" : 0,
"test" : "some",
"test3" : {
"some-other-key" : {
"more-nested-data" : true,
"notEmptyArr" : [
"XXX",
{
"corn" : "dog"
},
{
"other" : {
"zap" : "notNull"
}
}
]
}
}
}
A convenient way to debug such complex functions is by declaring them as variables outside of the pipeline and running data through them to simulate the documents (objects) coming out the database, e.g.:
ff = function(obj) {
var process = function(holder, spot, value) {
var remove_it = false;
// test FIRST since [] instanceof Object is true!
if(Array.isArray(value)) {
...
printjson(ff(dd)); // use the same doc as above
You can put print and other debugging aids into the code and then when you are done, you can remove them and call the pipeline to process the real data as follows:
db.foo.aggregate([
{$replaceRoot: {newRoot: {$function: {
body: ff, // substitute here!
args: [ "$$CURRENT" ],
lang: "js"
}}
}}
]);
Sounds like the unwind operator would help. Checkout the unwind operator at https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
Are there any data types in GraphQL that can be used to describe a JSON Patch operation?
The structure of a JSON Patch operation is as follows.
{ "op": "add|replace|remove", "path": "/hello", "value": ["world"] }
Where value can be any valid JSON literal or object, such as.
"value": { "name": "michael" }
"value": "hello, world"
"value": 42
"value": ["a", "b", "c"]
op and path are always simple strings, value can be anything.
If you need to return JSON type then graphql have scalar JSON
which return any JSON type where you want to return it.
Here is schema
`
scalar JSON
type Response {
status: Boolean
message: String
data: JSON
}
type Test {
value: JSON
}
type Query {
getTest: Test
}
type Mutation {
//If you want to mutation then pass data as `JSON.stringify` or json formate
updateTest(value: JSON): Response
}
`
In resolver you can return anything in json format with key name "value"
//Query resolver
getTest: async (_, {}, { context }) => {
// return { "value": "hello, world" }
// return { "value": 42 }
// return { "value": ["a", "b", "c"] }
// return anything in json or string
return { "value": { "name": "michael" } }
},
// Mutation resolver
async updateTest(_, { value }, { }) {
// Pass data in JSON.stringify
// value : "\"hello, world\""
// value : "132456"
// value : "[\"a\", \"b\", \"c\"]"
// value : "{ \"name\": \"michael\" }"
console.log( JSON.parse(value) )
//JSON.parse return formated required data
return { status: true,
message: 'Test updated successfully!',
data: JSON.parse(value)
}
},
the only thing you need to specifically return "value" key to identify to get in query and mutation
Query
{
getTest {
value
}
}
// Which return
{
"data": {
"getTest": {
"value": {
"name": "michael"
}
}
}
}
Mutation
mutation {
updateTest(value: "{ \"name\": \"michael\" }") {
data
status
message
}
}
// Which return
{
"data": {
"updateTest": {
"data": null,
"status": true,
"message": "success"
}
}
}
I have one intent in my lambda function. I am trying to fill 4 slots, 3 of which are required. In my tests it seems like I have to set the Assignee field to a default or something fails in my actual handler which happens after the else statement below. Here's how I'm currently defining the defaults:
if strings.ToUpper(request.DialogState) == "STARTED" {
log.Println("DialogState == STARTED")
// Pre-fill slots: update the intent object with slot values for which
// you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property.
slots := make(map[string]alexa.IntentSlot)
slots["Summary"] = alexa.IntentSlot{
Name: "Summary",
Value: "",
ConfirmationStatus: "NONE",
}
slots["TicketType"] = alexa.IntentSlot{
Name: "TicketType",
Value: "",
ConfirmationStatus: "NONE",
}
slots["Project"] = alexa.IntentSlot{
Name: "Project",
Value: "",
ConfirmationStatus: "NONE",
}
slots["Assignee"] = alexa.IntentSlot{
Name: "Assignee",
Value: "tcheek",
ConfirmationStatus: "NONE",
}
i := &alexa.Intent{
Name: "OpenTicketIntent",
ConfirmationStatus: "NONE",
Slots: slots,
}
response.AddDialogDirective("Dialog.Delegate", "", "", i)
response.ShouldSessionEnd = false
log.Println("DialogState has exited STARTED")
} else if strings.ToUpper(request.DialogState) != "COMPLETED" {
log.Println("DialogState == IN PROGRESS")
// return a Dialog.Delegate directive with no updatedIntent property.
response.ShouldSessionEnd = false
response.AddDialogDirective("Dialog.Delegate", "", "", nil)
log.Println("DialogState has exited IN PROGRESS")
} else {
I have also tried setting just the Assignee field as a default, like this:
slots := make(map[string]alexa.IntentSlot)
slots["Assignee"] = alexa.IntentSlot{
Name: "Assignee",
Value: "tcheek",
ConfirmationStatus: "NONE",
}
i := &alexa.Intent{
Name: "OpenTicketIntent",
ConfirmationStatus: "NONE",
Slots: slots,
}
response.AddDialogDirective("Dialog.Delegate", "", "", i)
In this scenario I get the following lambda function response in the simulator:
{
"body": {
"version": "1.0",
"response": {
"directives": [
{
"type": "Dialog.Delegate",
"updatedIntent": {
"name": "OpenTicketIntent",
"confirmationStatus": "NONE",
"slots": {
"Assignee": {
"name": "Assignee",
"value": "tcheek",
"confirmationStatus": "NONE"
}
}
}
}
],
"shouldEndSession": false
}
}
}
The problem is that once I ask it to open a bug ticket (which maps to the intent with the "Open a {ticketType} ticket" utterance), it gives the response that "There was a problem with the requested skill's response".
Am I wrong to think that setting defaults is necessary? Am I setting defaults incorrectly?
As per my knowledge in response, u need to include all slots. As here the intent name and slots match then only you'll get the correct response
I can't retrieve the form data using Revel. I'm able to retrieve query params though.
I have this controller to test the content of c.Params:
func (c UserController) SaveUser() revel.Result {
return c.RenderJson(c.Params) //just for check the content
}
When I pass a query param (testkey, value) I get:
{
"Values": {
"testkey": [
"value"
]
},
"Fixed": null,
"Route": null,
"Query": {
"testkey": [
"value"
]
},
"Form": null,
"Files": null
}
Everything ok. But when I pass a form param and I don't get any data:
{
"Values": {},
"Fixed": null,
"Route": null,
"Query": {},
"Form": null,
"Files": null
}
It is a PUT request and I'm using Postman to pass form params.
Thanks.
I found the solution. I had to pass form params as method parameters:
// id and nickname are form params
func (c UserController) SaveUser(id, nickname string) revel.Result {
return c.RenderJson(c.Params)
}