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

@@ -3834,8 +3834,10 @@ int AST_OPTIONAL_API_NAME(ast_agi_register)(struct ast_module *mod, agi_command
if (ast_strlen_zero(cmd->summary) && ast_strlen_zero(cmd->usage)) {
#ifdef AST_XML_DOCS
*((char **) &cmd->summary) = ast_xmldoc_build_synopsis("agi", fullcmd, NULL);
*((char **) &cmd->since) = ast_xmldoc_build_since("agi", fullcmd, NULL);
*((char **) &cmd->usage) = ast_xmldoc_build_description("agi", fullcmd, NULL);
*((char **) &cmd->syntax) = ast_xmldoc_build_syntax("agi", fullcmd, NULL);
*((char **) &cmd->arguments) = ast_xmldoc_build_arguments("agi", fullcmd, NULL);
*((char **) &cmd->seealso) = ast_xmldoc_build_seealso("agi", fullcmd, NULL);
*((enum ast_doc_src *) &cmd->docsrc) = AST_XML_DOC;
#endif
@@ -3882,12 +3884,16 @@ int AST_OPTIONAL_API_NAME(ast_agi_unregister)(agi_command *cmd)
#ifdef AST_XML_DOCS
if (e->docsrc == AST_XML_DOC) {
ast_free((char *) e->summary);
ast_free((char *) e->since);
ast_free((char *) e->usage);
ast_free((char *) e->syntax);
ast_free((char *) e->arguments);
ast_free((char *) e->seealso);
*((char **) &e->summary) = NULL;
*((char **) &e->since) = NULL;
*((char **) &e->usage) = NULL;
*((char **) &e->syntax) = NULL;
*((char **) &e->arguments) = NULL;
*((char **) &e->seealso) = NULL;
}
#endif
@@ -4346,72 +4352,66 @@ static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cl
if (a->argc > e->args - 1) {
command = find_command(a->argv + e->args, 1);
if (command) {
char *synopsis = NULL, *description = NULL, *syntax = NULL, *seealso = NULL;
char info[30 + MAX_CMD_LEN]; /* '-= Info about...' */
char infotitle[30 + MAX_CMD_LEN + AST_TERM_MAX_ESCAPE_CHARS]; /* '-= Info about...' with colors */
char syntitle[11 + AST_TERM_MAX_ESCAPE_CHARS]; /* [Syntax]\n with colors */
char desctitle[15 + AST_TERM_MAX_ESCAPE_CHARS]; /* [Description]\n with colors */
char deadtitle[13 + AST_TERM_MAX_ESCAPE_CHARS]; /* [Runs Dead]\n with colors */
char deadcontent[3 + AST_TERM_MAX_ESCAPE_CHARS]; /* 'Yes' or 'No' with colors */
char seealsotitle[12 + AST_TERM_MAX_ESCAPE_CHARS]; /* [See Also]\n with colors */
char stxtitle[10 + AST_TERM_MAX_ESCAPE_CHARS]; /* [Syntax]\n with colors */
size_t synlen, desclen, seealsolen, stxlen;
term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, sizeof(syntitle));
term_color(desctitle, "[Description]\n", COLOR_MAGENTA, 0, sizeof(desctitle));
term_color(deadtitle, "[Runs Dead]\n", COLOR_MAGENTA, 0, sizeof(deadtitle));
term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, sizeof(seealsotitle));
term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, sizeof(stxtitle));
term_color(deadcontent, command->dead ? "Yes" : "No", COLOR_CYAN, 0, sizeof(deadcontent));
char *synopsis = NULL, *since = NULL, *description = NULL, *syntax = NULL, *arguments = NULL, *seealso = NULL;
ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
snprintf(info, sizeof(info), "\n -= Info about agi '%s' =- ", fullcmd);
term_color(infotitle, info, COLOR_CYAN, 0, sizeof(infotitle));
#ifdef AST_XML_DOCS
if (command->docsrc == AST_XML_DOC) {
synopsis = ast_xmldoc_printable(S_OR(command->summary, "Not available"), 1);
since = ast_xmldoc_printable(S_OR(command->since, "Not available"), 1);
description = ast_xmldoc_printable(S_OR(command->usage, "Not available"), 1);
syntax = ast_xmldoc_printable(S_OR(command->syntax, "Not available"), 1);
arguments = ast_xmldoc_printable(S_OR(command->arguments, "Not available"), 1);
seealso = ast_xmldoc_printable(S_OR(command->seealso, "Not available"), 1);
if (!seealso || !description || !synopsis) {
error = 1;
goto return_cleanup;
}
} else
#endif
{
synlen = strlen(S_OR(command->summary, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
synopsis = ast_malloc(synlen);
desclen = strlen(S_OR(command->usage, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
description = ast_malloc(desclen);
seealsolen = strlen(S_OR(command->seealso, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
seealso = ast_malloc(seealsolen);
if (!synopsis || !description || !seealso) {
error = 1;
goto return_cleanup;
}
term_color(synopsis, S_OR(command->summary, "Not available"), COLOR_CYAN, 0, synlen);
term_color(description, S_OR(command->usage, "Not available"), COLOR_CYAN, 0, desclen);
term_color(seealso, S_OR(command->seealso, "Not available"), COLOR_CYAN, 0, seealsolen);
synopsis = ast_strdup(S_OR(command->summary, "Not Available"));
since = ast_strdup(S_OR(command->since, "Not Available"));
description = ast_strdup(S_OR(command->usage, "Not Available"));
syntax = ast_strdup(S_OR(command->syntax, "Not Available"));
arguments = ast_strdup(S_OR(command->arguments, "Not Available"));
seealso = ast_strdup(S_OR(command->seealso, "Not Available"));
}
stxlen = strlen(S_OR(command->syntax, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
syntax = ast_malloc(stxlen);
if (!syntax) {
if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
error = 1;
goto return_cleanup;
}
term_color(syntax, S_OR(command->syntax, "Not available"), COLOR_CYAN, 0, stxlen);
ast_cli(a->fd, "%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n", infotitle, stxtitle, syntax,
desctitle, description, syntitle, synopsis, deadtitle, deadcontent,
seealsotitle, seealso);
ast_cli(a->fd, "\n"
"%s -= Info about AGI '%s' =- %s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n"
COLORIZE_FMT "\n"
"%s\n\n",
ast_term_color(COLOR_MAGENTA, 0), fullcmd, ast_term_reset(),
COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
COLORIZE(COLOR_MAGENTA, 0, "[Runs Dead]"), command->dead ? "Yes" : "No",
COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
);
return_cleanup:
ast_free(synopsis);
ast_free(since);
ast_free(description);
ast_free(syntax);
ast_free(arguments);
ast_free(seealso);
} else {
if (find_command(a->argv + e->args, -1)) {