docs: Enable since/version handling for XML, CLI and ARI documentation

* Added the "since" element to the XML configObject and configOption elements
  in appdocsxml.dtd.

* Added the "Since" section to the following CLI output:
  ```
  config show help <module> <object>
  config show help <module> <object> <option>
  core show application <app>
  core show function <func>
  manager show command <command>
  manager show event <event>
  agi show commands topic <topic>
  ```

* Refactored the commands above to output their sections in the same order:
  Synopsis, Since, Description, Syntax, Arguments, SeeAlso

* Refactored the commands above so they all use the same pattern for writing
  the output to the CLI.

* Fixed several memory leaks caused by failure to free temporary output
  buffers.

* Added a "since" array to the mustache template for the top-level resources
  (Channel, Endpoint, etc.) and to the paths/methods underneath them. These
  will be added to the generated markdown if present.
  Example:
  ```
    "resourcePath": "/api-docs/channels.{format}",
    "requiresModules": [
        "res_stasis_answer",
        "res_stasis_playback",
        "res_stasis_recording",
        "res_stasis_snoop"
    ],
    "since": [
        "18.0.0",
        "21.0.0"
    ],
    "apis": [
        {
            "path": "/channels",
            "description": "Active channels",
            "operations": [
                {
                    "httpMethod": "GET",
                    "since": [
                        "18.6.0",
                        "21.8.0"
                    ],
                    "summary": "List all active channels in Asterisk.",
                    "nickname": "list",
                    "responseClass": "List[Channel]"
                },

  ```

NOTE:  No versioning information is actually added in this commit.
Those will be added separately and instructions for adding and maintaining
them will be published on the documentation site at a later date.

(cherry picked from commit 3e28ddce78)
This commit is contained in:
George Joseph
2025-01-09 15:17:14 -07:00
committed by Asterisk Development Team
parent d468918359
commit 24c077f1fb
13 changed files with 493 additions and 272 deletions

View File

@@ -1722,6 +1722,91 @@ char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *m
return output;
}
/*!
* \internal
* \brief Build since information for an item
*
* \param node The since node to parse
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \retval A malloc'd character pointer to the since information of the item
* \retval NULL on failure
*
* \since 22
*/
static char *_ast_xmldoc_build_since(struct ast_xml_node *node)
{
char *output;
struct ast_str *outputstr;
const char *content;
int first = 1;
/* Find the <since> node. */
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (!strcasecmp(ast_xml_node_get_name(node), "since")) {
break;
}
}
if (!node || !ast_xml_node_get_children(node)) {
/* we couldnt find a <since> node. */
return NULL;
}
/* prepare the output string. */
outputstr = ast_str_create(128);
if (!outputstr) {
return NULL;
}
/* get into the <since> node. */
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (strcasecmp(ast_xml_node_get_name(node), "version")) {
continue;
}
content = ast_xml_get_text(node);
if (!content) {
continue;
}
ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
first = 0;
ast_xml_free_text(content);
}
output = ast_strdup(ast_str_buffer(outputstr));
ast_free(outputstr);
return output;
}
char *ast_xmldoc_build_since(const char *type, const char *name, const char *module)
{
char *output;
struct ast_xml_node *node;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
return NULL;
}
/* get the application/function root node. */
AST_RWLIST_RDLOCK(&xmldoc_tree);
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node || !ast_xml_node_get_children(node)) {
AST_RWLIST_UNLOCK(&xmldoc_tree);
return NULL;
}
output = _ast_xmldoc_build_since(node);
AST_RWLIST_UNLOCK(&xmldoc_tree);
return output;
}
/*!
* \internal
* \brief Parse a \<enum\> node.
@@ -2286,11 +2371,12 @@ static void ast_xml_doc_item_destructor(void *obj)
return;
}
ast_free(doc->syntax);
ast_free(doc->seealso);
ast_free(doc->arguments);
ast_free(doc->synopsis);
ast_free(doc->since);
ast_free(doc->description);
ast_free(doc->syntax);
ast_free(doc->arguments);
ast_free(doc->seealso);
ast_string_field_free_memory(doc);
if (AST_LIST_NEXT(doc, next)) {
@@ -2318,11 +2404,13 @@ static struct ast_xml_doc_item *ast_xml_doc_item_alloc(const char *name, const c
return NULL;
}
if ( !(item->syntax = ast_str_create(128))
|| !(item->seealso = ast_str_create(128))
if ( !(item->synopsis = ast_str_create(128))
|| !(item->since = ast_str_create(128))
|| !(item->description = ast_str_create(128))
|| !(item->syntax = ast_str_create(128))
|| !(item->arguments = ast_str_create(128))
|| !(item->synopsis = ast_str_create(128))
|| !(item->description = ast_str_create(128))) {
|| !(item->seealso = ast_str_create(128))
) {
ast_log(AST_LOG_ERROR, "Failed to allocate strings for ast_xml_doc_item instance\n");
goto ast_xml_doc_item_failure;
}
@@ -2381,44 +2469,50 @@ static int ast_xml_doc_item_cmp(void *obj, void *arg, int flags)
static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_node *node, const char *name, const char *type)
{
struct ast_xml_doc_item *item;
char *syntax;
char *seealso;
char *arguments;
char *synopsis;
char *since;
char *description;
char *syntax;
char *arguments;
char *seealso;
if (!(item = ast_xml_doc_item_alloc(name, type))) {
return NULL;
}
item->node = node;
syntax = _ast_xmldoc_build_syntax(node, type, name);
seealso = _ast_xmldoc_build_seealso(node);
arguments = _ast_xmldoc_build_arguments(node);
synopsis = _ast_xmldoc_build_synopsis(node);
since = _ast_xmldoc_build_since(node);
description = _ast_xmldoc_build_description(node);
syntax = _ast_xmldoc_build_syntax(node, type, name);
arguments = _ast_xmldoc_build_arguments(node);
seealso = _ast_xmldoc_build_seealso(node);
if (syntax) {
ast_str_set(&item->syntax, 0, "%s", syntax);
}
if (seealso) {
ast_str_set(&item->seealso, 0, "%s", seealso);
}
if (arguments) {
ast_str_set(&item->arguments, 0, "%s", arguments);
}
if (synopsis) {
ast_str_set(&item->synopsis, 0, "%s", synopsis);
}
if (since) {
ast_str_set(&item->since, 0, "%s", since);
}
if (description) {
ast_str_set(&item->description, 0, "%s", description);
}
if (syntax) {
ast_str_set(&item->syntax, 0, "%s", syntax);
}
if (arguments) {
ast_str_set(&item->arguments, 0, "%s", arguments);
}
if (seealso) {
ast_str_set(&item->seealso, 0, "%s", seealso);
}
ast_free(syntax);
ast_free(seealso);
ast_free(arguments);
ast_free(synopsis);
ast_free(since);
ast_free(description);
ast_free(syntax);
ast_free(arguments);
ast_free(seealso);
return item;
}