How to access a reactive object? Shiny Login Example

Viewed 320

I found this example of login on this link, but I had a question: how do I access the user who is logged in? I realized that this information is stored in the object called auth, but how to access it without giving an error?

# NOT RUN {
if (interactive()) {
  
  library(shiny)
  library(shinymanager)
  
  # data.frame with credentials info
  credentials <- data.frame(
    user = c("1","fanny", "victor"),
    password = c("1","azerty", "12345"),
    comment = c("1","alsace", "auvergne"),
    stringsAsFactors = FALSE
  )
  
  # app
  ui <- fluidPage(
    
    # authentication module
    auth_ui(
      id = "auth",
      # add image on top ?
      tags_top = 
        tags$div(
          tags$h4("Demo", style = "align:center"),
          tags$img(
            src = "https://www.r-project.org/logo/Rlogo.png", width = 100
          )
        ),
      # add information on bottom ?
      tags_bottom = tags$div(
        tags$p(
          "For any question, please  contact ",
          tags$a(
            href = "mailto:someone@example.com?Subject=Shiny%20aManager",
            target="_top", "administrator"
          )
        )
      ),
      # change auth ui background ?
      background  = "linear-gradient(rgba(0, 0, 255, 0.5),
                       rgba(255, 255, 0, 0.5)),
                       url('https://www.r-project.org/logo/Rlogo.png');"
    ),
    
    # result of authentication
    verbatimTextOutput(outputId = "res_auth"),
    
    # classic app
    headerPanel('Iris k-means clustering'),
    sidebarPanel(
      selectInput('xcol', 'X Variable', names(iris)),
      selectInput('ycol', 'Y Variable', names(iris),
                  selected=names(iris)[[2]]),
      numericInput('clusters', 'Cluster count', 3,
                   min = 1, max = 9)
    ),
    mainPanel(
      plotOutput('plot1')
    )
  )
  
  server <- function(input, output, session) {
    
    # authentication module
    auth <- callModule(
      module = auth_server,
      id = "auth",
      check_credentials = check_credentials(credentials)
    )
    
    output$res_auth <- renderPrint({
      reactiveValuesToList(auth) ## <---- this line print which user is logged in

    })
    
    # classic app
    selectedData <- reactive({
      
      req(auth$result)  # <---- dependency on authentication result
      
      iris[, c(input$xcol, input$ycol)]
    })
    
    clusters <- reactive({
      kmeans(selectedData(), input$clusters)
    })
    
    output$plot1 <- renderPlot({
      palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
                "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
      
      par(mar = c(5.1, 4.1, 0, 1))
      plot(selectedData(),
           col = clusters()$cluster,
           pch = 20, cex = 3)
      points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
    })
  }
  
  shinyApp(ui, server)
  
}

how to access auth$user? subscript out of bounds error is happening, I wanted to access the user who is in the "auth" object that is being printed inside the app on the line marked "<-------this line print which user is logged in". it seems to me an error on how to use a reactive object.

My personal case: I am trying to send a query to a MySQL database, as follows:

  1. Attempt 1 (using user_data() )
user_data <- reactive({
    req(auth$result)
    auth$user
  })
connection<-reactivePoll( intervalMillis = 300,session,checkFunc = function(){
    storiesDb <- dbConnect(RMariaDB::MariaDB(), user='USER', password=localuserpassword, dbname='USER', host='localhost')
    querysel1=reactive({paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",user_data(),"' ",sep= ''
    )})
    rs = dbSendQuery(storiesDb,querysel1)
    
    dbFetch(rs) },
    valueFunc = function(){
      
      querysel1=reactive({paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",user_data(),"' ",sep= ''
                      
      )}) 
      rs = dbSendQuery(storiesDb,querysel1)
      dbFetch(rs)
    }
  )

I tried to use user_data(), and gave the error: "unable to find an inherited method for function ‘dbSendQuery’ for signature ‘"MariaDBConnection", "reactiveExpr"’'"

  1. Attempt 2 (without using user_data() )
connection<-reactivePoll( intervalMillis = 300,session,checkFunc = function(){
    storiesDb <- dbConnect(RMariaDB::MariaDB(), user='USER', password=localuserpassword, dbname='USER', host='localhost')
    querysel1=reactive({paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''
    )})
    rs = dbSendQuery(storiesDb,querysel1)

    dbFetch(rs) },
    valueFunc = function(){

      querysel1=reactive({paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''

      )}) 
      rs = dbSendQuery(storiesDb,querysel1)
      dbFetch(rs)
    }
  )

I tried to use auth$user, and gave the error: "Error in as.vector (x," character "): cannot coerce type 'closure' to vector of type 'character'"

  1. Attempt 3 (without reactive in querysel1)
connection<-reactivePoll( intervalMillis = 300,session,checkFunc = function(){
    storiesDb <- dbConnect(RMariaDB::MariaDB(), user='USER', password=localuserpassword, dbname='USER', host='localhost')
    querysel1=paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''
    )
    rs = dbSendQuery(storiesDb,querysel1)

    dbFetch(rs) },
    valueFunc = function(){

      querysel1=paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''

      ) 
      rs = dbSendQuery(storiesDb,querysel1)
      dbFetch(rs)
    }
  )

I tried without reactive in querysel1, and gave the empty error: " Error : "

It seems to me that all these errors happen because of the reactive object inside the server.

2 Answers

Edit

Thanks for giving more context with reactivePoll, I think I found the issue:

The problem here lies in the execution of reactivePoll. When you start the app, reactivePoll already starts executing, but no user is logged in yet. That means that auth$user does not exist yet (it is NULL) and the code in your checkFun and valueFun can't handle that. I've included a small example (use user = 1 and password = 1) to demonstrate that in principle it works. I made sure that I don't execute the code as long as auth$user is NULL:

library(shiny)
library(shinymanager)

# data.frame with credentials info
credentials <- data.frame(
  user = c("1","fanny", "victor"),
  password = c("1","azerty", "12345"),
  comment = c("1","alsace", "auvergne"),
  stringsAsFactors = FALSE
)

# app
ui <- fluidPage(
  
  # authentication module
  auth_ui(
    id = "auth",
    # add image on top ?
    tags_top = 
      tags$div(
        tags$h4("Demo", style = "align:center"),
        tags$img(
          src = "https://www.r-project.org/logo/Rlogo.png", width = 100
        )
      ),
    # add information on bottom ?
    tags_bottom = tags$div(
      tags$p(
        "For any question, please  contact ",
        tags$a(
          href = "mailto:someone@example.com?Subject=Shiny%20aManager",
          target="_top", "administrator"
        )
      )
    ),
    # change auth ui background ?
    background  = "linear-gradient(rgba(0, 0, 255, 0.5),
                       rgba(255, 255, 0, 0.5)),
                       url('https://www.r-project.org/logo/Rlogo.png');"
  ),
  
  # result of authentication
  verbatimTextOutput(outputId = "res_auth"),
  
  # classic app
  headerPanel('Iris k-means clustering'),
  sidebarPanel(
    selectInput('xcol', 'X Variable', names(iris)),
    selectInput('ycol', 'Y Variable', names(iris),
                selected=names(iris)[[2]]),
    numericInput('clusters', 'Cluster count', 3,
                 min = 1, max = 9)
  ),
  mainPanel(
    plotOutput('plot1'),
    textOutput("user_name")
  )
)

server <- function(input, output, session) {
  
  # authentication module
  auth <- callModule(
    module = auth_server,
    id = "auth",
    check_credentials = check_credentials(credentials)
  )
  
  output$res_auth <- renderPrint({
    reactiveValuesToList(auth) ## <---- this line print which user is logged in
    
  })
  
  # the following line is just an example how to use auth$user in a different
  # reactive
  user_data <- reactive({
    auth$user
  })
  
  # call the new reactive in a render function
  output$user_name <- renderText({
    paste0("The user currently logged in is: ", user_data())
  })
  
  # classic app
  selectedData <- reactivePoll(intervalMillis = 1000,
                           session,
                           checkFunc = function() {
                             if (!is.null(auth$user) && auth$user == "1") {
                               rnorm(1)
                             } else {
                               1
                             }
                           },
                           valueFunc = function() {
                             n_row <- sample(1:150, 120)
                             iris[n_row, c(input$xcol, input$ycol)]
                           })
  
  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })
  
  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
    
    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })
}

shinyApp(ui, server)

I'm not sure why, but simply adding req(auth$user) does not work here.

You could do something like:

connection<-reactivePoll( intervalMillis = 300,
                          session,
                          checkFunc = function(){
                            if (!is.null(auth$user)) {
                              storiesDb <- dbConnect(RMariaDB::MariaDB(), user='USER', password=localuserpassword, dbname='USER', host='localhost')
                              querysel1=paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''
                              )
                              rs = dbSendQuery(storiesDb,querysel1)
                              
                              dbFetch(rs)
                            } else {
                              NULL
                            }
                          },
                          valueFunc = function(){
                            if (!is.null(auth$user)) {
                              querysel1=paste("SELECT COL1
                 FROM   TABLENAME
                 where id ='",auth$user,"' ",sep= ''
                                              
                              ) 
                              rs = dbSendQuery(storiesDb,querysel1)
                              dbFetch(rs)
                            } else {
                              NULL
                            }
                          }
)

Here, I just return NULL as long as auth$user does not exist, you can adapt this to your needs.


my old answer:

I'm not sure where exactly your problem/error happens. For me, your example works. I've added another example how to access auth$user. As it is a reactive, you can only access it in a reactive context.

library(shiny)
library(shinymanager)

# data.frame with credentials info
credentials <- data.frame(
  user = c("1","fanny", "victor"),
  password = c("1","azerty", "12345"),
  comment = c("1","alsace", "auvergne"),
  stringsAsFactors = FALSE
)

# app
ui <- fluidPage(
  
  # authentication module
  auth_ui(
    id = "auth",
    # add image on top ?
    tags_top = 
      tags$div(
        tags$h4("Demo", style = "align:center"),
        tags$img(
          src = "https://www.r-project.org/logo/Rlogo.png", width = 100
        )
      ),
    # add information on bottom ?
    tags_bottom = tags$div(
      tags$p(
        "For any question, please  contact ",
        tags$a(
          href = "mailto:someone@example.com?Subject=Shiny%20aManager",
          target="_top", "administrator"
        )
      )
    ),
    # change auth ui background ?
    background  = "linear-gradient(rgba(0, 0, 255, 0.5),
                       rgba(255, 255, 0, 0.5)),
                       url('https://www.r-project.org/logo/Rlogo.png');"
  ),
  
  # result of authentication
  verbatimTextOutput(outputId = "res_auth"),
  
  # classic app
  headerPanel('Iris k-means clustering'),
  sidebarPanel(
    selectInput('xcol', 'X Variable', names(iris)),
    selectInput('ycol', 'Y Variable', names(iris),
                selected=names(iris)[[2]]),
    numericInput('clusters', 'Cluster count', 3,
                 min = 1, max = 9)
  ),
  mainPanel(
    plotOutput('plot1'),
    textOutput("user_name")
  )
)

server <- function(input, output, session) {
  
  # authentication module
  auth <- callModule(
    module = auth_server,
    id = "auth",
    check_credentials = check_credentials(credentials)
  )
  
  output$res_auth <- renderPrint({
    reactiveValuesToList(auth) ## <---- this line print which user is logged in
    
  })
  
  # the following line is just an example how to use auth$user in a different
  # reactive
  user_data <- reactive({
    auth$user
  })
  
  # call the new reactive in a render function
  output$user_name <- renderText({
    paste0("The user currently logged in is: ", user_data())
  })
  
  # classic app
  selectedData <- reactive({
    
    req(auth$result)  # <---- dependency on authentication result
    
    iris[, c(input$xcol, input$ycol)]
  })
  
  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })
  
  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
    
    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })
}

shinyApp(ui, server)

To elaborate on my comment, maybe its better to show the code:

See in the comments, which statements are working and which won't. I just used user victor with pass 12345 and did not check whether it is working with other credentials.

library(shiny)
library(shinymanager)

# data.frame with credentials info
credentials <- data.frame(
  user = c("1","fanny", "victor"),
  password = c("1","azerty", "12345"),
  comment = c("1","alsace", "auvergne"),
  stringsAsFactors = FALSE
)

# app
ui <- fluidPage(
  
  # authentication module
  auth_ui(
    id = "auth",
    # add image on top ?
    tags_top = 
      tags$div(
        tags$h4("Demo", style = "align:center"),
        tags$img(
          src = "https://www.r-project.org/logo/Rlogo.png", width = 100
        )
      ),
    # add information on bottom ?
    tags_bottom = tags$div(
      tags$p(
        "For any question, please  contact ",
        tags$a(
          href = "mailto:someone@example.com?Subject=Shiny%20aManager",
          target="_top", "administrator"
        )
      )
    ),
    # change auth ui background ?
    background  = "linear-gradient(rgba(0, 0, 255, 0.5),
                       rgba(255, 255, 0, 0.5)),
                       url('https://www.r-project.org/logo/Rlogo.png');"
  ),
  
  # result of authentication
  verbatimTextOutput(outputId = "res_auth"),
  
  # classic app
  headerPanel('Iris k-means clustering'),
  sidebarPanel(
    selectInput('xcol', 'X Variable', names(iris)),
    selectInput('ycol', 'Y Variable', names(iris),
                selected=names(iris)[[2]]),
    numericInput('clusters', 'Cluster count', 3,
                 min = 1, max = 9)
  ),
  mainPanel(
    plotOutput('plot1')
  )
)

server <- function(input, output, session) {
  
  # authentication module
  auth <- callModule(
    module = auth_server,
    id = "auth",
    check_credentials = check_credentials(credentials)
  )
  
  output$res_auth <- renderPrint({
    # reactiveValuesToList(auth$user) ## <---- not working
    auth[["user"]] ## <----  working
    # auth$user ## <----  this works too 
    # reactiveValuesToList(auth)[["user"]] # <--- this works too 
    
  })
  
  # classic app
  selectedData <- reactive({
    
    req(auth$result)  # <---- dependency on authentication result
    
    iris[, c(input$xcol, input$ycol)]
  })
  
  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })
  
  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
    
    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })
}

shinyApp(ui, server)

}
Related