Is there a way to use the API to modify content tags? I'm trying to write a pins client which uploads a file and then also applies the correct tags to that file.
Hi @gordon,
Thanks for your post. We don't currently have this functionality available in the public Content management API for RStudio Connect. Have you worked with the experimental content API to programmatically manage content on Connect before? https://docs.rstudio.com/connect/api/#content
I would be interested in hearing more about your use case here. Have you been applying tags manually so far? Is tagging an important part of the deployment process for new content? And is this a one-time programmatic action, or do you need to update tags programmatically over time?
I did take a look at the experimental API, but couldn't figure out how to update the tags parameter.
The use case is that we are using pins to store data and want to automatically apply tags to that data based on things like which folder it's in. We have been applying tags manually so far, but we want to do this automatically so that the user who's pinning the data doesn't need to think about it.
Thanks for sharing the use case there! It is definitely helpful to see the types of workflows our users are aiming for!
Do you mind sharing your thoughts on how you expect to specify the tags that you want to set? I.e. what type of interface would you expect for specifying the tag(s) for a given pin? What would you expect to happen if the tag you specify did not exist? This can be tricky with nested tag structures, etc. Are these top level tags you are using, or nested within some other hierarchy? (i.e. you can have tags with the same name in different hierarchies)
Apologies for the barrage of questions there. I'd really love to get a picture of what the "ideal workflow" looks like in your mind, especially since this is a currently nonexistent feature!
Ah, okay so there are basically two api cases here:
Tags for a single piece of content
In this case I think the top level API should require and return a nested list of tags for the piece of content. So if you asked for tags(content)
you might get back something like:
list(
tag1 = list(
tag2 = "Tom"
tag3 = "Walter"
)
tag2 = "Harry"
)
Then if you wanted to set the different tag2
's you would use:
tags(content) <- list(tag2 = "New Harry")
tags(content) <- list(tag1 = list(tag2 = "New Walter"))
Then you could build some convenience functions into the R package which would make it easier to construct the API response where there weren't any duplicated tags. I.e. the user would supply something like:
tags(content) <- list(tag3 = "Walter")
tags(content)$tag3 <- "New Waler"
But you would convert that to the full nested structure before posting it.
tags(content) <- list(tag1 = list(tag3 = "New Waler"))
Navigating tags
I'm not exactly sure how to navigate nested tags programatically, but you might take some inspiration from this folder based API which Neal developed at Crunch: https://github.com/Crunch-io/rcrunch/blob/master/vignettes/projects.Rmd It accomplished a similar job and worked very well.
Interesting!! Thanks for the thoughtful overview there! I will definitely take a look at the example you provided (although I have not yet). Just to be clear here, what do the names here represent? tag3
vs. tag1
? Is there any meaning there?
Oh yes sorry my example makes no sense. I think just replace all the character names with logicals. So a piece of content might be tagged:
list(
tag1 = list(
tag2 = TRUE
tag3 = FALSE
)
tag2 = TRUE
)
And on the R package side you could accept either character or logical vectors like this:
# copy tags
tags(content1) <- tags(content2)
# apply these subtags
tags(content)$tag1 <- c("tag1", "tag2")
# remove tag2
tags(content)$tag2 <- FALSE
# remove all tags
tags(content) <- FALSE
Very interesting! Thanks for sharing! These are some really good thoughts on what an R CLI/SDK for managing tags would look like! This will definitely be super useful as we explore this implementation. Please continue sharing if you have any additional ideas!! And we will keep you updated as we make changes to this functionality!
I do like the idea of being able to set the tags programmatically in this manner but I think that the nested aspect of this method may get confusing if a server has a more complex hierarchy of tags. For example, if you have even a third layer this may get confusing:
tags
+-- Fuaada
| +-- Amber
| | +-- Shaquille
| | \-- Kyle
| \-- Abigail
\-- Maegen
+-- Samani
\-- Sarah
+-- Mariah
+-- Tucker
\-- Flor
Would you apply the Shaquille
tag like this:
tags(content)$Fuaada$Amber <- "Shaquille"
or
tags(content)$Fuaada$Amber$Shaquille <- TRUE
I think this is pretty confusing. It might be better to make the application of tags to applications not rely on the hierarchical structure. So the "Shaquille" tag would simply be applied like this:
tags(content)$Shaquille <- TRUE
I don't know if this would cause issues with duplication of tags. For example, if there are two "Shaquille" tags, one under the "Amber" tag and another under the "Sarah" tag. I am not sure whether RStudio Connect allows multiple identical tags in different branches of the hierarchy? If it does then some other specification would have to considered - maybe just specifying its parent tag?
Anyway, just my thoughts, curious to hear what you all think!
I don't have any fantastic ideas for an "ideal" application process here, but I can comment that RStudio Connect will allow a "Shaquille" tag under "Amber" and under "Sarah." You cannot have two "Shaquille"s under "Amber," but in separate trees, "duplicate" tag names are definitely allowed. You could even have a "Shaquille" under "Amber" and "Abigail" and "Sarah" and "Fuaada." All the Shaquilles!
could be something like:
apply_tag(content, tag, parent_tag)
and if parent_tag is NULL
and there are multiple instances of the tag specified with tag
then it throws an error
I think with both the tag and the parent_tag (if needed) the API could be used to obtain the tag id which could then be used for the actual application of the tag to the specific content
My experience with Crunch is that the best way to do this is to do the following:
- Expose the sever API (Send a json list of tags to update)
- Build a high level, R framework that matches the api as closely as possible (send a named list of tags to update)
- Write convenience functions around the high level framework which make the user's life easy. (
tag(content) <-
,apply_tag
etc)
I've found this to be the easiest way to go, mostly because you usually need to experiment with step 3 to get something that works well.
Much delayed, but I now have a proposed (but not completely designed) interface that I would like y'all's feedback on
client <- connect()
myapp <- content_item(client, "my-guid")
tags <- get_tags(client)
get_content_tags(myapp)
set_content_tags(myapp, tags$Section$tag$subtag, tags$Section$tag$anothertag)
Any immediate thoughts? The $
pattern has the benefit of autocomplete when you are doing things interactively. It is perhaps less than ideal for programmatic things. I envisioned something like this for programmatic:
create_tag_tree(client, "Section", "tag", "subtag")
set_content_tag_tree(myapp, "Section", "tag", "subtag")
Ooh... we could even use rlang
to drop the quotes