# How should I browse tags using the java API? Bugs?

**URL:** <https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029>\
**Category:** Module Development\
**Created:** [April 4, 2023, 4:00pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029 "2023-04-04T16:00:05Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Andrew\_Cunningham](https://avatars.discourse-cdn.com/v4/letter/a/9f8e36/32.png) [@Andrew\_Cunningham](https://forum.inductiveautomation.com/u/Andrew_Cunningham)\
**Post date:** [April 4, 2023, 4:00pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/1 "2023-04-04T16:00:05Z")

</div>

Hi all.  
I'm trying to write a module component which reads all tag configurations in ignition on module start (TODO: have it periodically rescan) in order to look for custom configuration attributes and act on them.

I'm encountering something odd; the java API isn't returning all of the tags I expect.

Using a python gateway event, I'm able to run:

```python
def browse(prefix, node, next):
  if node is None:
    return
  prefix = prefix + "/" + (node.get("name") or "aryastark") # A node has no name
  if "tags" not in node:
  	print "Tagchange script:", prefix, "=", {k:v for k,v in node.items() if k != "tags"}
  	return
  for child in node["tags"]:
  	next(prefix, child, next)

root = "[default]/Software/LatencyTest" # Or whatever. Can also be emptystring.
print "Begin Tagchange script!", root
for node in system.tag.getConfiguration(root, True):
  print "Tagchange script: Toplevel", (node.get("name") or "aryastark")
  browse(root, node, browse)
print "End Tagchange script!"

```

(the combinator is because recursion didn't work without it).

However, when I run equivalent code (kotlin; it runs from my module's startup hook: )

```kotlin
            fun fetchAll(
                tagManager:GatewayTagManager,
                sink: (TagPath) -> Unit
            ) {
                fun browseNode(prefix: TagPath, tagConfig: TagConfigurationModel) {
                    val fullPath = prefix.getChildPath(tagConfig.path.toStringPartial()) // For some reason the tagConfig.path is a partial path, not a FQPath -- bug? Misnamed field? Needs doc either way.
                    when (tagConfig.type) {
                        TagObjectType.Folder, TagObjectType.Provider -> for (child in tagConfig.children) browseNode(fullPath, child)
                        TagObjectType.AtomicTag -> sink(BaseTag(service, tagConfig.path))
                        else -> logger.error("Unrecognized type $fullPath!")
                    }
                }
                val root = TagPathParser.parse("") // I know that this is different, but it is a prefix of the python code...
                runBlocking {
                    for (provider in tagManager.tagProviders) {
                        for (firstGeneration in provider.getTagConfigsAsync(listOf(root), true, false).get(30, TimeUnit.SECONDS)) {
                            browseNode(root, firstGeneration)
                        }
                    }
                }
            }

```

Python has all of the tags I can see in the ignition designer.  
However, java (kotlin...) is missing several random tags, with no apparent pattern between them!  
From a spot check, this java code has e.g. `[default]Software/LatencyTest/read/887/Data`, but not `[default]Software/LatencyTest/read/0/Data` or `[default]Software/LatencyTest/read/1/Data` (which are afaik identical tags). It doesn't matter if they are initialized; they're all readonly, memory, etc.

At this point I guess I assume startup order: perhaps ignition doesn't load the full set of tags before loading modules or something, so that my late-running python code can see more than my (relatively) early-running java code.  
Is this documented? Is there a method to call (or point during execution?) where I can get the same snapshot the designer & python code has of tags in my java module?

Thank you!

---

<div class="post-metadata">

**Author:** ![Andrew\_Cunningham](https://avatars.discourse-cdn.com/v4/letter/a/9f8e36/32.png) [@Andrew\_Cunningham](https://forum.inductiveautomation.com/u/Andrew_Cunningham)\
**Post date:** [April 5, 2023, 2:18pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/2 "2023-04-05T14:18:13Z")

</div>

An update.  
I changed the code to do this reload & print step every 15s after module load.  
The error is persistent: the same keys are still (and always) missing; this means startup ordering is not implicated.

Help!! 🙂

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [April 5, 2023, 2:39pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/3 "2023-04-05T14:39:00Z")

</div>

I pinged a developer more familiar with this, but he was out sick yesterday and I'm not sure if he'll be here today or not.

I saw you opened a support ticket for this as well, but I'll just let you know now that the support department doesn't assist with module development issues and there is no official support avenue, just the forum.

---

<div class="post-metadata">

**Author:** ![mrojas](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mrojas/32/6394_2.png) [@mrojas](https://forum.inductiveautomation.com/u/mrojas)\
**Post date:** [April 5, 2023, 3:26pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/4 "2023-04-05T15:26:56Z")

</div>

The scripting functions and module tag apis end up calling the same functions, so I'm not sure where the discrepancy is. Can you send me a tag export so I can double check a few things? Also, it doesn't sound like you're using them, but `TagObjectType.UdtInstance` and `TagObjectType.UdtType` can also contain children tags, so if you run into them you'll be losing their children with current code.

---

<div class="post-metadata">

**Author:** ![Andrew\_Cunningham](https://avatars.discourse-cdn.com/v4/letter/a/9f8e36/32.png) [@Andrew\_Cunningham](https://forum.inductiveautomation.com/u/Andrew_Cunningham)\
**Post date:** [April 5, 2023, 5:47pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/5 "2023-04-05T17:47:09Z")

</div>

My tags are: [tags.json.zip](https://forum.inductiveautomation.com/uploads/short-url/75dk4cP1ZxsCLCX7j31JDWCRieb.zip) (411.6 KB).  
Note all desired tags do appear there.  
More testing: The missing .../0/Data and .../1/Data tags _might_ appear in my output logs... ... with the wrong names. I have two .../1/Data and zero .../0/Data in my output, this time -- still a bug, but a different bug!

Some questions that maybe indicate I'm doing something wrong:

1. how do I actually generate the full path to the node recursively? I _think_ that the `TagConfigurationModel.getPath()` is literally the same string as `TagConfigurationModel.getName()`, just wrapped in a (misleading?) `TagPath`; I think that what I should do is take in the parent `TagPath` and use `parentTagPath.getChildPath(child.getName())` -- except in the case where `parentTagPath.getPathLength() == 0 || parentTagPath.getLastPathComponent.isEmpty()`, which means the parent was "" which means these are top level folders, and I should just use `child.getPath()` straight, because `getChildPath` doesn't handle that. True?
2. What's the best way to get a specific instance of `Property<*>` (such as `Property<DataType>` named `dataType` so that I can read the tag's data type...)? `Property` is an interface, and the concrete subclass constructors are too specific to implementation for me to feel confident in constructing an instance to use to get information out of the `TagConfigurationModel` correctly. Currently I get an instance from the first object, cache it, and use it on the following objects -- acceptable or sketchy?

Totally agreed wrt support of structured tags in this code; gotta leave some problems for myself in the future.

---

<div class="post-metadata">

**Author:** ![mrojas](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mrojas/32/6394_2.png) [@mrojas](https://forum.inductiveautomation.com/u/mrojas)\
**Post date:** [April 5, 2023, 7:46pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/6 "2023-04-05T19:46:06Z")

</div>

1. `getPath()` from `TagConfigurationModel` is relative to it's parent, so that's why you're only seeing the child name in there. I'd do what you mentioned and just use `parentPath.getChildPath(child.getName())` to build the full path. You shouldn't have to worry about a child path with length == 0 if you use this method. Your starting parent path in this case will always be `root`
2. `WellKnownTagProps` should contain the majority of Properties you're dealing with, if you have a custom property you'll have to create a `BasicProperty` with the specific value type you're expecting.

---

<div class="post-metadata">

**Author:** ![Andrew\_Cunningham](https://avatars.discourse-cdn.com/v4/letter/a/9f8e36/32.png) [@Andrew\_Cunningham](https://forum.inductiveautomation.com/u/Andrew_Cunningham)\
**Post date:** [April 6, 2023, 8:58pm UTC](https://forum.inductiveautomation.com/t/how-should-i-browse-tags-using-the-java-api-bugs/72029/7 "2023-04-06T20:58:10Z")

</div>

Ok. I can say that this path (using `child.getName`) does work; if anyone else would find the code helpful, my version automatically recurses into (only) folders:

```kotlin
fun GatewayTagManager.streamTags(
    visit: (TagProvider) -> ((TagPath, TagConfigurationModel) -> Boolean)?
) {
    fun browseNode(visit: (TagPath, TagConfigurationModel)->Boolean, fullPath: TagPath, tagConfig: TagConfigurationModel) {
        if (visit(fullPath, tagConfig)) when (tagConfig.type) {
                TagObjectType.Folder -> {
                    for (child in tagConfig.children) {
                        browseNode(visit, fullPath.getChildPath(child.name), child)
                    }
                }
                else -> return
            }
        }
    }
    runBlocking {
        for (provider in tagProviders) {
            val root = TagPathParser.parse("[${provider.name}]") // Though one might use some subdir in some cases...
            val visitor = visit(provider)
                ?: continue
            val firstGenerations = provider.getTagConfigsAsync(listOf(root), true, false).get(30, TimeUnit.SECONDS)
            for (firstGeneration in firstGenerations) {
                when (firstGeneration.type) {
                    TagObjectType.Provider -> for (secondGeneration in firstGeneration.children) {
                        browseNode(visitor, root.getChildPath(secondGeneration.name), secondGeneration)
                    }
                    else -> browseNode(visitor, root.getChildPath(firstGeneration.name), firstGeneration)
                }
            }
        }
    }
}

```
