public Tag tag(ObjectNode node, String location, ParseResult result) {
    Tag tag = null;

    if (node != null) {
      tag = new Tag();
      Set<String> keys = getKeys(node);

      String value = getString("name", node, true, location, result);
      tag.name(value);

      value = getString("description", node, false, location, result);
      tag.description(value);

      ObjectNode externalDocs = getObject("externalDocs", node, false, location, result);
      ExternalDocs docs = externalDocs(externalDocs, location + "externalDocs", result);
      tag.externalDocs(docs);

      // extra keys
      for (String key : keys) {
        if (key.startsWith("x-")) {
          tag.setVendorExtension(key, extension(node.get(key)));
        } else if (!TAG_KEYS.contains(key)) {
          result.extra(location + ".externalDocs", key, node.get(key));
        }
      }
    }

    return tag;
  }
  private void parse(
      Swagger swagger, RestDefinition rest, String camelContextId, ClassResolver classResolver) {
    List<VerbDefinition> verbs = new ArrayList<>(rest.getVerbs());
    // must sort the verbs by uri so we group them together when an uri has multiple operations
    Collections.sort(verbs, new VerbOrdering());

    // we need to group the operations within the same tag, so use the path as default if not
    // configured
    String pathAsTag =
        rest.getTag() != null ? rest.getTag() : FileUtil.stripLeadingSeparator(rest.getPath());
    String summary = rest.getDescriptionText();

    if (ObjectHelper.isNotEmpty(pathAsTag)) {
      // add rest as tag
      Tag tag = new Tag();
      tag.description(summary);
      tag.name(pathAsTag);
      swagger.addTag(tag);
    }

    // gather all types in use
    Set<String> types = new LinkedHashSet<>();
    for (VerbDefinition verb : verbs) {
      String type = verb.getType();
      if (ObjectHelper.isNotEmpty(type)) {
        if (type.endsWith("[]")) {
          type = type.substring(0, type.length() - 2);
        }
        types.add(type);
      }
      type = verb.getOutType();
      if (ObjectHelper.isNotEmpty(type)) {
        if (type.endsWith("[]")) {
          type = type.substring(0, type.length() - 2);
        }
        types.add(type);
      }
      // there can also be types in response messages
      if (verb.getResponseMsgs() != null) {
        for (RestOperationResponseMsgDefinition def : verb.getResponseMsgs()) {
          type = def.getResponseModel();
          if (ObjectHelper.isNotEmpty(type)) {
            if (type.endsWith("[]")) {
              type = type.substring(0, type.length() - 2);
            }
            types.add(type);
          }
        }
      }
    }

    // use annotation scanner to find models (annotated classes)
    for (String type : types) {
      Class<?> clazz = classResolver.resolveClass(type);
      appendModels(clazz, swagger);
    }

    // used during gathering of apis
    List<Path> paths = new ArrayList<>();

    String basePath = rest.getPath();

    for (VerbDefinition verb : verbs) {

      // the method must be in lower case
      String method = verb.asVerb().toLowerCase(Locale.US);
      // operation path is a key
      String opPath = SwaggerHelper.buildUrl(basePath, verb.getUri());

      Operation op = new Operation();
      if (ObjectHelper.isNotEmpty(pathAsTag)) {
        // group in the same tag
        op.addTag(pathAsTag);
      }

      // add id as vendor extensions
      op.getVendorExtensions().put("x-camelContextId", camelContextId);
      op.getVendorExtensions().put("x-routeId", verb.getRouteId());

      Path path = swagger.getPath(opPath);
      if (path == null) {
        path = new Path();
        paths.add(path);
      }
      path = path.set(method, op);

      String consumes = verb.getConsumes() != null ? verb.getConsumes() : rest.getConsumes();
      if (consumes != null) {
        String[] parts = consumes.split(",");
        for (String part : parts) {
          op.addConsumes(part);
        }
      }

      String produces = verb.getProduces() != null ? verb.getProduces() : rest.getProduces();
      if (produces != null) {
        String[] parts = produces.split(",");
        for (String part : parts) {
          op.addProduces(part);
        }
      }

      if (verb.getDescriptionText() != null) {
        op.summary(verb.getDescriptionText());
      }

      for (RestOperationParamDefinition param : verb.getParams()) {
        Parameter parameter = null;
        if (param.getType().equals(RestParamType.body)) {
          parameter = new BodyParameter();
        } else if (param.getType().equals(RestParamType.form)) {
          parameter = new FormParameter();
        } else if (param.getType().equals(RestParamType.header)) {
          parameter = new HeaderParameter();
        } else if (param.getType().equals(RestParamType.path)) {
          parameter = new PathParameter();
        } else if (param.getType().equals(RestParamType.query)) {
          parameter = new QueryParameter();
        }

        if (parameter != null) {
          parameter.setName(param.getName());
          parameter.setAccess(param.getAccess());
          parameter.setDescription(param.getDescription());
          parameter.setRequired(param.getRequired());

          // set type on parameter
          if (parameter instanceof SerializableParameter) {
            SerializableParameter sp = (SerializableParameter) parameter;

            if (param.getDataType() != null) {
              sp.setType(param.getDataType());
            }
            if (param.getAllowableValues() != null) {
              sp.setEnum(param.getAllowableValues());
            }
          }

          // set schema on body parameter
          if (parameter instanceof BodyParameter) {
            BodyParameter bp = (BodyParameter) parameter;

            if (verb.getType() != null) {
              String ref = modelTypeAsRef(verb.getType(), swagger);
              if (ref != null) {
                bp.setSchema(new RefModel(ref));
              }
            }
          }

          op.addParameter(parameter);
        }
      }

      // if we have an out type then set that as response message
      if (verb.getOutType() != null) {
        Response response = new Response();
        Property prop = modelTypeAsProperty(verb.getOutType(), swagger);
        response.setSchema(prop);
        response.setDescription("Output type");
        op.addResponse("200", response);
      }

      // enrich with configured response messages from the rest-dsl
      for (RestOperationResponseMsgDefinition msg : verb.getResponseMsgs()) {
        Response response = null;
        if (op.getResponses() != null) {
          response = op.getResponses().get(msg.getCode());
        }
        if (response == null) {
          response = new Response();
        }
        if (ObjectHelper.isNotEmpty(msg.getResponseModel())) {
          Property prop = modelTypeAsProperty(msg.getResponseModel(), swagger);
          response.setSchema(prop);
        }
        response.setDescription(msg.getMessage());
        op.addResponse(msg.getCode(), response);
      }

      // add path
      swagger.path(opPath, path);
    }
  }
  /** Builds the document header of the swagger model */
  private void overview() {
    Info info = swagger.getInfo();
    this.markupDocBuilder.documentTitle(info.getTitle());
    this.markupDocBuilder.sectionTitleLevel1(OVERVIEW);
    if (isNotBlank(info.getDescription())) {
      this.markupDocBuilder.textLine(info.getDescription());
    }
    if (isNotBlank(info.getVersion())) {
      this.markupDocBuilder.sectionTitleLevel2(CURRENT_VERSION);
      this.markupDocBuilder.textLine(VERSION + info.getVersion());
    }
    Contact contact = info.getContact();
    if (contact != null) {
      this.markupDocBuilder.sectionTitleLevel2(CONTACT_INFORMATION);
      if (isNotBlank(contact.getName())) {
        this.markupDocBuilder.textLine(CONTACT_NAME + contact.getName());
      }
      if (isNotBlank(contact.getEmail())) {
        this.markupDocBuilder.textLine(CONTACT_EMAIL + contact.getEmail());
      }
    }

    License license = info.getLicense();
    if (license != null && (isNotBlank(license.getName()) || isNotBlank(license.getUrl()))) {
      this.markupDocBuilder.sectionTitleLevel2(LICENSE_INFORMATION);
      if (isNotBlank(license.getName())) {
        this.markupDocBuilder.textLine(LICENSE + license.getName());
      }
      if (isNotBlank(license.getUrl())) {
        this.markupDocBuilder.textLine(LICENSE_URL + license.getUrl());
      }
    }
    if (isNotBlank(info.getTermsOfService())) {
      this.markupDocBuilder.textLine(TERMS_OF_SERVICE + info.getTermsOfService());
    }

    if (isNotBlank(swagger.getHost())
        || isNotBlank(swagger.getBasePath())
        || isNotEmpty(swagger.getSchemes())) {
      this.markupDocBuilder.sectionTitleLevel2(URI_SCHEME);
      if (isNotBlank(swagger.getHost())) {
        this.markupDocBuilder.textLine(HOST + swagger.getHost());
      }
      if (isNotBlank(swagger.getBasePath())) {
        this.markupDocBuilder.textLine(BASE_PATH + swagger.getBasePath());
      }
      if (isNotEmpty(swagger.getSchemes())) {
        List<String> schemes = new ArrayList<>();
        for (Scheme scheme : swagger.getSchemes()) {
          schemes.add(scheme.toString());
        }
        this.markupDocBuilder.textLine(SCHEMES + join(schemes, ", "));
      }
    }

    if (isNotEmpty(swagger.getTags())) {
      this.markupDocBuilder.sectionTitleLevel2(TAGS);
      List<String> tags = new ArrayList<>();
      for (Tag tag : swagger.getTags()) {
        String name = tag.getName();
        String description = tag.getDescription();
        if (isNoneBlank(description)) {
          tags.add(name + ": " + description);
        } else {
          tags.add(name);
        }
      }
      this.markupDocBuilder.unorderedList(tags);
    }

    if (isNotEmpty(swagger.getConsumes())) {
      this.markupDocBuilder.sectionTitleLevel2(CONSUMES);
      this.markupDocBuilder.unorderedList(swagger.getConsumes());
    }

    if (isNotEmpty(swagger.getProduces())) {
      this.markupDocBuilder.sectionTitleLevel2(PRODUCES);
      this.markupDocBuilder.unorderedList(swagger.getProduces());
    }
  }