index method

  1. @override
Future<String> index({
  1. bool showPublic = false,
})
override

Generates and returns OpenAPI documentation as a JSON string.

The showPublic parameter determines whether to show the documentation publicly. If set to false, documentation is only shown in local debug mode.

Returns a Future that completes with a JSON string containing the OpenAPI 3.1 documentation.

Implementation

@override
Future<String> index({bool showPublic = false}) async {
  var config = WaServer.config;
  if (!showPublic && !config.isLocalDebug) {
    return rq.renderError(403);
  }

  var paths = {};
  List<WebRoute> routes = await server.getAllRoutes(rq);

  var webRoutes = _convert(routes, '', null);

  for (var route in webRoutes) {
    if (route.apiDoc == null) {
      continue;
    }

    var apiDoc = await route.apiDoc!.call();

    if (!route.path.startsWith('/api') || apiDoc == null) {
      continue;
    }
    var res = {};

    for (var method in route.methods) {
      var data = {};
      RegExp regex = RegExp(r'{.*?}');
      data['tags'] = [
        //pathNorm(route.path, normSlashs: true),
        pathNorm(route.path.replaceAll(regex, ''), normSlashs: true),
      ];
      data['summary'] = route.title;
      data['description'] = route.title;
      data['operationId'] = "${route.title} [$method] ${route.path}";
      if (route.auth != null) {
        data['security'] = [
          {
            "auth": ["write", "read"]
          }
        ];
      }
      var requestBody = {};
      var responses = {};
      var parameters = [];

      var doc = ApiDoc(
        body: List.from(apiDoc.body),
        response: Map.from(apiDoc.response),
        parameters: List.from(apiDoc.parameters),
        description: apiDoc.description,
        post: apiDoc.post,
        delete: apiDoc.delete,
        get: apiDoc.get,
        put: apiDoc.put,
      );

      if (method.toLowerCase() == 'get' && doc.get != null) {
        doc.body.addAll(doc.get!.body);
        doc.response.addAll(doc.get!.response);
        doc.parameters.addAll(doc.get!.parameters);
        doc.description = doc.get!.description;
      } else if (method.toLowerCase() == 'post' && doc.post != null) {
        doc.body.addAll(doc.post!.body);
        doc.response.addAll(doc.post!.response);
        doc.parameters.addAll(doc.post!.parameters);
        doc.description = doc.post!.description;
      } else if (method.toLowerCase() == 'put' && doc.put != null) {
        doc.body.addAll(doc.put!.body);
        doc.response.addAll(doc.put!.response);
        doc.parameters.addAll(doc.put!.parameters);
        doc.description = doc.put!.description;
      } else if (method.toLowerCase() == 'delete' && doc.delete != null) {
        doc.body.addAll(doc.delete!.body);
        doc.response.addAll(doc.delete!.response);
        doc.parameters.addAll(doc.delete!.parameters);
        doc.description = doc.delete!.description;
      }

      if (doc.body.isNotEmpty) {
        var responseShema = <dynamic, dynamic>{
          "type": "object",
        };
        var properties = {};
        var description = "";
        for (var pro in doc.body) {
          properties[pro.name] = {
            'type': pro.typeString,
            'format': pro.typeString,
            'examples': [pro.def],
          };

          description +=
              "<b>${pro.name}</b>: ${pro.isRequired ? '* Is required' : 'Not required'} ${pro.description != null ? '| $pro.description' : ""}\n\n";
        }
        responseShema['type'] = 'object';
        responseShema['properties'] = properties;
        requestBody['description'] = description;
        requestBody['content'] = {
          'application/json': {
            'schema': responseShema,
          },
        };
      }

      if (doc.response.isNotEmpty) {
        for (var res in doc.response.keys) {
          var listResponse = doc.response[res]!;
          var responseShema = {};
          var properties = {};
          for (var response in listResponse) {
            properties[response.name] = {
              'type': response.typeString,
              'format': response.typeString,
              'examples': [response.def],
            };
          }
          responseShema['type'] = 'object';
          responseShema['properties'] = properties;
          responses[res] = {
            'description': res,
            'content': {
              'application/json': {
                'schema': responseShema,
              },
            },
          };
        }
      }

      for (var param in doc.parameters) {
        parameters = [
          ...parameters,
          {
            "name": param.name,
            "in": param.paramIn.toString(),
            "description": param.description ?? "",
            "required": param.isRequired,
            "schema": {
              "type": param.typeString,
              "format": param.typeString,
            }
          },
        ];
      }

      data['responses'] = responses;
      data['requestBody'] = requestBody;
      data['parameters'] = parameters;

      res[method.toLowerCase()] = data;
    }
    var standardPath = route.path;
    if (paths.containsKey(standardPath)) {
      res = {...paths[standardPath], ...res};
    }
    paths = {...paths, standardPath: res};
  }

  var data = {
    "openapi": "3.1.0",
    "info": {
      "title": "$title - OpenAPI 3.1",
      "description":
          "WebApp Api documentation maker v${WaServer.info.version}",
      "contact": {"email": "info@uproid.com"},
      "license": {
        "name": "Apache 2.0",
        "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
      },
      "version": config.version
    },
    "externalDocs": {
      "description": "Find out more about WebApp",
      "url": rq.url("/")
    },
    "servers": [
      {"url": rq.url("/")}
    ],
    "paths": {
      ...paths,
    },
    "components": {
      "securitySchemes": {
        "auth": {
          "type": "http",
          "scheme": "bearer",
          "bearerFormat": "JWT",
          "name": security,
          "in": "header"
        },
      }
    }
  };

  return rq.renderData(data: data);
}