diff --git a/.changeset/fix-github-url-parsing.md b/.changeset/fix-github-url-parsing.md new file mode 100644 index 000000000..a9f9b44b5 --- /dev/null +++ b/.changeset/fix-github-url-parsing.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/cli": patch +--- + +fix: support slash-based branch names and multi-dot file extensions in GitHub URL parsing diff --git a/src/apps/api/middlewares/validation.middleware.ts b/src/apps/api/middlewares/validation.middleware.ts index 244d6f808..885cfb89b 100644 --- a/src/apps/api/middlewares/validation.middleware.ts +++ b/src/apps/api/middlewares/validation.middleware.ts @@ -54,14 +54,17 @@ async function compileAjv(options: ValidationMiddlewareOptions) { const requestBody = method.requestBody; if (!requestBody) { - return; + return; // No request body defined - validation not needed } - let schema = requestBody.content['application/json'].schema; - if (!schema) { - return; + // Check if application/json content type exists + const jsonContent = requestBody.content?.['application/json']; + if (!jsonContent || !jsonContent.schema) { + return; // No JSON schema defined - validation not needed } + let schema = jsonContent.schema; + schema = { ...schema }; schema['$schema'] = 'http://json-schema.org/draft-07/schema'; @@ -173,16 +176,11 @@ export async function validationMiddleware( } try { - if (!validate) { - throw new ProblemException({ - type: 'invalid-request-body', - title: 'Invalid Request Body', - status: 422, - detail: `Request body validation is not supported for "${options.path}" path with "${options.method}" method.`, - }); + // If no requestBody schema is defined, skip validation + // This is valid - not all endpoints require request bodies + if (validate) { + await validateRequestBody(validate, req.body); } - - await validateRequestBody(validate, req.body); } catch (error: unknown) { if (error instanceof ProblemException) { return next(error); diff --git a/src/domains/models/SpecificationFile.ts b/src/domains/models/SpecificationFile.ts index 5370f6e67..80c377063 100644 --- a/src/domains/models/SpecificationFile.ts +++ b/src/domains/models/SpecificationFile.ts @@ -237,11 +237,12 @@ export async function fileExists(name: string): Promise { return true; } - const extension = name.split('.')[1]; + // Get file extension properly (handle multiple dots like my.asyncapi.yaml) + const extension = name.split('.').pop()?.toLowerCase(); const allowedExtenstion = ['yml', 'yaml', 'json']; - if (!allowedExtenstion.includes(extension)) { + if (!extension || !allowedExtenstion.includes(extension)) { throw new ErrorLoadingSpec('invalid file', name); } diff --git a/src/domains/services/validation.service.ts b/src/domains/services/validation.service.ts index 11ac45fc4..023395dce 100644 --- a/src/domains/services/validation.service.ts +++ b/src/domains/services/validation.service.ts @@ -69,13 +69,37 @@ const convertGitHubWebUrl = (url: string): string => { const urlWithoutFragment = url.split('#')[0]; // Handle GitHub web URLs like: https://github.com/owner/repo/blob/branch/path - // eslint-disable-next-line no-useless-escape - const githubWebPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.+)$/; - const match = urlWithoutFragment.match(githubWebPattern); - - if (match) { - const [, owner, repo, branch, filePath] = match; - return `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}?ref=${branch}`; + // Support branch names with slashes (e.g., feature/new-validation) + const githubUrlPrefix = 'https://github.com/'; + if (urlWithoutFragment.startsWith(githubUrlPrefix)) { + const afterPrefix = urlWithoutFragment.slice(githubUrlPrefix.length); + const parts = afterPrefix.split('/'); + + if (parts.length >= 4 && parts[2] === 'blob') { + const owner = parts[0]; + const repo = parts[1]; + // Everything after 'blob/' is branch + file path + const branchAndPath = parts.slice(3).join('/'); + + // Find the file path by looking for the last segment with an extension + // or assume the last segment is the file + const segments = branchAndPath.split('/'); + let filePathIndex = segments.length - 1; + + // Work backwards to find where file path likely starts + // (look for segments with file extensions) + for (let i = segments.length - 1; i >= 0; i--) { + if (segments[i].includes('.') || i === segments.length - 1) { + filePathIndex = i; + break; + } + } + + const branch = segments.slice(0, filePathIndex).join('/'); + const filePath = segments.slice(filePathIndex).join('/'); + + return `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}?ref=${branch}`; + } } return url;