I have this code in my application, I use goroutine because that queries are very slow. The code works correctly but I've been trying to test that function with sqlmock and that been having a lot of false positives and a lot of dificulties, what can I do to test those queries using sqlmock? (Obs: I've used postgres)
func(r repository) Queries(ctx context.Context) (*models.MyModel, error) {
var users int64
var services int64
var sells int64
queryReponses := make(chan *gorm.DB, 3)
go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM deposits").Find(&users)
}()
go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM services").Find(&users)
}()
go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM sells").Find(&users)
}()
for i := 0; i < 3; i += 1 {
queryResponse := <-queryReponses
if queryResponse.Error != nil {
return &models.MyModel{}, fmt.Errorf(queryResponse.Error)
}
}
return &models.MyModel{
Users: users,
Services: services,
Sells: sells,
}
}
You modify the same value concurrently, run your tests with a -race detector flag to detect it
go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM deposits").Find(&users)
}()
- go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM services").Find(&users)
+ go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM services").Find(&services)
}()
- go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM sells").Find(&users)
}()
+ go func(){
queryResponses <- r.db.Raw("SELECT COUNT(*) FROM sells").Find(&sells)
}()
also for such a case the sync/errgroup is more convenient to use:
var g errgroup.Group
g.Go(func() error {
return r.db.Raw("SELECT COUNT(*) FROM deposits").Find(&users).Error
})
g.Go(func() error {
return r.db.Raw("SELECT COUNT(*) FROM services").Find(&services).Error
})
if err := g.Wait(); err == nil {
return &models.MyModel{}, fmt.Errorf(queryResponse.Error)
}
Related
I'm trying to write a module that gets a reactive dataframe as an input and allows the user to manipulate it (for the sake of the Minimal Reproducible Example, to add to the table a single row)
Initially, the data that being passed to the module from the main app is some default dataframe (hard coded in the MRE), so the module is always initiated with data.
In addition, I also want to allow the user to manipulate that data from outside the module (for the sake of the MRE, override the dataset with a different, hard-coded dataset).
I cannot make both functionalities in the MRE to work at the same time. At the moment, the update from main app works, but the update from within the module won't work. I found some solutions that would enable the opposite situation.
when trying to add row: no response and no error.
Note 1: The use of modules in the MRE is artificial and not really needed, but it is very much needed in the real app.
Note2: returning a new data frame instead of updating it is not ideal in my case as I would want to allow the user other manipulations, and only after all changes are done, to return the the new data frame.
Minimal Reproducible Example:
library(shiny)
library(tidyverse)
DEFAULT_DATA <- tribble(
~letter, ~number,
"A", 1,
"B", 2,
)
changeDataUI <- function(id) {
ns <- NS(id)
tagList(
tableOutput(ns("tbl"))
,br()
,actionButton(ns("add_row"), 'Add Row')
)
}
changeDataServer <- function(id, data) {
moduleServer(
id,
function(input, output, session) {
observeEvent(input$add_row, {
data <- data() %>% add_row(letter = "C", number = 3)
})
output$tbl <- renderTable(data())
}
)
}
ui <- fluidPage(
titlePanel("MRE App")
,fluidRow(column(6, actionButton("change_dataset", "Change Dataset")))
,fluidRow(column(6, changeDataUI("some_id")))
)
server <- function(input, output) {
glob_rvs <- reactiveValues(data = DEFAULT_DATA)
observeEvent(input$change_dataset, {
glob_rvs$data <- tribble(
~letter, ~number,
"D", 4,
"E", 5,
)
})
changeDataServer(id = "some_id", data = reactive(glob_rvs$data))
}
shinyApp(ui = ui, server = server)
With R, you typically want your modules to act as functional as possible. This, as you point out allows you to better reason about your app. I would have your module return the rows to be added and then have your top level app append them. Otherwise you module is essentially causing side effects. Also, this way your top level app (or another module) could coordinate multiple manipulations. The module could still show the data
Example implementation for module server:
changeDataServer <- function(id, data) {
moduleServer(
id,
function(input, output, session) {
additionalRows <- reactiveVal()
observeEvent(input$change, {
additionalRows(
data.frame(letter = sample(letters, 1) , number = runif(1, 0, 10))
)
})
output$tbl <- renderTable(data())
# return reactive with additional rows to allow to be merged at top level
additionalRows
}
)
}
Then update the server (also changed the input for the upload handler to match the UI (input$uploaded_data not input$uploaded_scheme)
server <- function(input, output) {
glob_rvs <- reactiveValues(data = DEFAULT_DATA)
observeEvent(input$uploaded_data, {
uploaded_data <- read_csv(input$uploaded_data$datapath)
glob_rvs$data <- uploaded_data
})
newRows <- changeDataServer(id = "some_id", data = reactive(glob_rvs$data))
observe({
glob_rvs$data <- bind_rows(glob_rvs$data, newRows())
}) %>%
bindEvent(newRows())
}
What you want to do here is to pass you reactiveValues object as an argument of your module server.
I advise you read this article about how to communicate data between modules
library(shiny)
library(tidyverse)
DEFAULT_DATA <- tribble(
~letter, ~number,
"A", 1,
"B", 2,
)
changeDataUI <- function(id) {
ns <- NS(id)
tagList(
tableOutput(ns("tbl"))
,br()
,actionButton(ns("change"), 'Add Row')
)
}
changeDataServer <- function(id, glob_rvs) {
moduleServer(
id,
function(input, output, session) {
observeEvent(input$change, {
print(glob_rvs$data)
glob_rvs$data <- glob_rvs$data %>% add_row(letter = "C", number = 3)
})
output$tbl <- renderTable(glob_rvs$data)
}
)
}
ui <- fluidPage(
titlePanel("MRE App")
,fluidRow(column(6,
fileInput("uploaded_data",
"would ypu like to upload your own data?",
multiple = FALSE,
accept = c(".csv"))))
,fluidRow(column(6, changeDataUI("some_id")))
)
server <- function(input, output) {
glob_rvs <- reactiveValues(data = DEFAULT_DATA)
observeEvent(input$uploaded_data, {
uploaded_data <- read_csv(input$uploaded_scheme$datapath)
glob_rvs$data <- uploaded_data
})
changeDataServer(id = "some_id", glob_rvs = glob_rvs)
}
shinyApp(ui = ui, server = server)
Following the solution by #Marcus, here is a working version that is consistent with the last version of post:
library(shiny)
library(tidyverse)
DEFAULT_DATA <- tribble(
~letter, ~number,
"A", 1,
"B", 2,
)
changeDataUI <- function(id) {
ns <- NS(id)
tagList(
tableOutput(ns("tbl"))
,br()
,actionButton(ns("add_row"), 'Add Row')
)
}
changeDataServer <- function(id, data) {
moduleServer(
id,
function(input, output, session) {
additionalRows <- reactiveVal()
observeEvent(input$add_row, {
additionalRows(
data.frame(letter = sample(letters, 1) , number = runif(1, 0, 10))
)
})
output$tbl <- renderTable(data())
# return reactive with additional rows to allow to be merged at top level
additionalRows
}
)
}
ui <- fluidPage(
titlePanel("MRE App")
,fluidRow(column(6, actionButton("change_dataset", "Change Dataset")))
,fluidRow(column(6, changeDataUI("some_id")))
)
server <- function(input, output) {
glob_rvs <- reactiveValues(data = DEFAULT_DATA)
observeEvent(input$change_dataset, {
glob_rvs$data <- tribble(
~letter, ~number,
"D", 4,
"E", 5,
)
})
newRows <- changeDataServer(id = "some_id", data = reactive(glob_rvs$data))
observe({
glob_rvs$data <- bind_rows(glob_rvs$data, newRows())
}) %>%
bindEvent(newRows())
}
shinyApp(ui = ui, server = server)
Is it possible to add and remove a mark line using a proxy so that the chart doesn't get fully redrawn?
To illustrate what it would look like:
library(shiny)
library(echarts4r)
df <- data.frame(
x = 1:100,
y = runif(100)
)
ui <- fluidPage(
actionButton("add", "Add series"),
actionButton("rm", "Remove series"),
echarts4rOutput("chart")
)
server <- function(input, output){
output$chart <- renderEcharts4r({
e_charts(df, x) %>%
e_scatter(y, z)
})
# e_mark_line() - has id added for this example
observeEvent(input$add, {
echarts4rProxy("chart", data = df, x = x) %>%
e_mark_line(
id = "my_line"
, data = list(xAxis = 50)
, title = "Line at 50") %>%
e_execute()
})
# e_remove_mark_line() - is made up for this example
observeEvent(input$rm, {
echarts4rProxy("chart") %>%
e_remove_mark_line("my_line")
})
}
shinyApp(ui, server)
It's a bit odd. Apparently, a 'mark line' is attached to a specific series. I didn't add handlers for the id field, it can be done, though. However, you would also have to specify the trace it's attached to.
BTW: in your code, you wrote e_scatter(y, z), but there is no z.
The easiest method is to create a function like the one you eluded to in your code.
There are two custom functions. One for Shiny in R code. One for the browser in Javascript. Combined, these create the function e_remove_markLine_p.
The R function (specifically for Shiny applications)
e_remove_markLine_p <- function (proxy)
{
opts <- list(id = proxy$id)
proxy$session$sendCustomMessage("e_remove_markLine_p", opts)
return(proxy)
}
The JS function
Shiny.addCustomMessageHandler('e_remove_markLine_p',
function(data) {
var chart = get_e_charts(data.id);
let opts = chart.getOption();
if(opts.markLine.length > 0) {
opts.markLine.length = 0; /* remove data */
}
chart.setOption(opts, true);
})
Using the power of Shiny, these two functions carry the request from the browser to R & back to the browser.
In the code, I've changed a few other things. Instead of e_mark_line, I used e_mark_p. I'm not sure if it matters, but per the documentation, that's the appropriate function.
Here's the entire app altogether.
library(tidyverse)
library(echarts4r)
library(shiny)
set.seed(315)
df <- data.frame(x = 1:100, y = runif(100))
# custom function for 'e_remove_markLine_p',
e_remove_markLine_p <- function (proxy)
{
opts <- list(id = proxy$id)
proxy$session$sendCustomMessage("e_remove_markLine_p", opts)
return(proxy)
}
ui <- fluidPage(
# adds the same call to both add and remove buttons
tags$head(
tags$script(HTML("
Shiny.addCustomMessageHandler('e_remove_markLine_p',
function(data) {
var chart = get_e_charts(data.id);
let opts = chart.getOption();
if(opts.markLine.length > 0) {
opts.markLine.length = 0; /* remove data */
}
chart.setOption(opts, true);
})
"))),
actionButton("add", "Add series"),
actionButton("rm", "Remove series"),
echarts4rOutput("chart")
)
server <- function(input, output){
output$chart <- renderEcharts4r({
e_charts(df, x) %>%
e_scatter(y) # <--- I removed z, since it doesn't exist...
})
observeEvent(input$add, {
echarts4rProxy("chart", data = df, x = x) %>%
e_mark_p(type = "line",
data = list(xAxis = 50),
title = "Line at 50") %>%
e_merge() %>% e_execute() # merge when adding to the plot
})
observeEvent(input$rm, {
echarts4rProxy("chart") %>%
e_remove_markLine_p() # remove all "mark" lines
})
}
shinyApp(ui, server) # show me what you got
In the below application, the edit works fine for the first time. (Say you click on first row button, it works fine. But once you cancel and again click on same button, it is not working)
Can anyone help me here? Is it related to binding ?
library(shiny)
ui <- fluidPage(
dataTableOutput("df")
)
server <- function(input, output, session) {
shinyInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
output$df <- renderDataTable({
iris$Edit = shinyInput(actionButton, nrow(iris), 'button_', label = "Edit", onclick = 'Shiny.onInputChange(\"select_button\", this.id)' )
datatable(iris, escape = F)
})
observeEvent(input$select_button,{
selectedRow <- as.numeric(strsplit(input$select_button, "_")[[1]][2])
showModal(modalDialog(
title = "Edit", size = "l",
fluidPage(width = 12,fluidRow(
column(width = 3,selectedRow)
)),
footer = tagList(modalButton("Cancel"),actionButton("update", "Update"))))
})
}
shinyApp(ui, server)
You should change this.id on this.id + "_" + Date.now()
When you generate unique this.id it works properly.
I'm using the following Go code to get only ONE registry in my dynamoDB table, but instead, it's returning all of them:
condition1 := expression.Name("id").Equal(expression.Value(id))
condition2 := expression.Name("apiKey").Equal(expression.Value(apiKey))
projection := expression.NamesList(
expression.Name("data"),
)
expr, err := expression.NewBuilder().
WithFilter(condition1).
WithFilter(condition2).
WithProjection(projection).
Build()
if err != nil {
return "", err
}
req := svc.ScanRequest(&dynamodb.ScanInput{
TableName: aws.String(awsEnv.Dynamo_Table),
ExpressionAttributeNames: expr.Names(),
ExpressionAttributeValues: expr.Values(),
FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
})
result, err := req.Send()
if err != nil {
// TODO
}
fmt.Println(result.Items)
This should contain only one registry, since there is only one registry that satisfies the id filter condition, but instead, I'm getting ALL registries.
Am I missing something?
The ScanInput exist attribute Setlimit (receive int64), you did not declare it:
req := svc.ScanRequest(&dynamodb.ScanInput{
Setlimit: 1,
TableName: aws.String(awsEnv.Dynamo_Table),
ExpressionAttributeNames: expr.Names(),
ExpressionAttributeValues: expr.Values(),
FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
})
read more:
https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/#ScanInput.SetLimit
I'm cutting my teeth on Go and after digging into table driven tests I ran into the following problem:
I have a function that returns multiple values
// Halves an integer and and returns true if it was even or false if it was odd.
func half(n int) (int, bool) {
h := n / 2
e := n%2 == 0
return h, e
}
I know that for half(1) the return value should be 0, false and for half(2) it should match 1, true, but I can't seem to figure out how to put this on a table.
How would one go to have something that resembles the following?
var halfTests = []struct {
in int
out string
}{
{1, <0, false>},
{3, <1, true>},
}
Is there any other, more idiomatic way of doing this?
For reference, here's a test for something that resembles a FizzBuzz function, using tables:
var fizzbuzzTests = []struct {
in int
out string
}{
{1, "1"},
{3, "Fizz"},
{5, "Buzz"},
{75, "FizzBuzz"},
}
func TestFizzBuzz(t *testing.T) {
for _, tt := range fizzbuzzTests {
s := FizzBuzz(tt.in)
if s != tt.out {
t.Errorf("Fizzbuzz(%d) => %s, want %s", tt.in, s, tt.out)
}
}
}
Just add another field to your struct that holds the second return value. Example:
var halfTests = []struct {
in int
out1 int
out2 bool
}{
{1, 0, false},
{3, 1, true},
}
Your testing function would look like the following:
func TestHalf(t *testing.T) {
for _, tt := range halfTests {
s, t := half(tt.in)
if s != tt.out1 || t != tt.out2 {
t.Errorf("half(%d) => %d, %v, want %d, %v", tt.in, s, t, tt.out1, tt.out2)
}
}
}