@@ -138,7 +138,7 @@ export async function validate(
138138 } else if ( jsonSchemaLocation ) {
139139 return await validatePatternOnly ( jsonSchemaLocation , metaSchemaPath , debug ) ;
140140 } else if ( jsonSchemaArchitectureLocation ) {
141- return await validateArchitectureOnly ( jsonSchemaArchitectureLocation ) ;
141+ return await validateArchitectureOnly ( jsonSchemaArchitectureLocation , metaSchemaPath , debug ) ;
142142 } else {
143143 logger . debug ( 'You must provide at least an architecture or a pattern' ) ;
144144 throw new Error ( 'You must provide at least an architecture or a pattern' ) ;
@@ -221,16 +221,87 @@ async function validatePatternOnly(jsonSchemaLocation: string, metaSchemaPath: s
221221
222222/**
223223 * Run the spectral validations for the case where only the architecture is provided.
224+ * When no pattern is provided, validate the architecture against the CALM schema specified in its $schema property.
224225 *
225226 * @param architectureSchemaLocation - The location of the architecture schema.
226- * @returns the validation outcome with the results of the spectral validation.
227+ * @param metaSchemaPath - The path of the meta schemas to use for ajv (optional).
228+ * @param debug - The flag to enable debug logging (optional).
229+ * @returns the validation outcome with the results of the spectral validation and JSON schema validation.
227230 */
228- async function validateArchitectureOnly ( architectureSchemaLocation : string ) : Promise < ValidationOutcome > {
229- logger . debug ( 'Pattern was not provided, only the Architecture will be validated ' ) ;
231+ async function validateArchitectureOnly ( architectureSchemaLocation : string , metaSchemaPath ?: string , debug : boolean = false ) : Promise < ValidationOutcome > {
232+ logger . debug ( 'Pattern was not provided, validating Architecture against its specified CALM schema ' ) ;
230233
231234 const jsonSchemaArchitecture = await getFileFromUrlOrPath ( architectureSchemaLocation ) ;
232- const spectralResultForArchitecture : SpectralResult = await runSpectralValidations ( jsonSchemaArchitecture , validationRulesForArchitecture ) ;
233- return new ValidationOutcome ( [ ] , spectralResultForArchitecture . spectralIssues , spectralResultForArchitecture . errors , spectralResultForArchitecture . warnings ) ;
235+ const spectralResultForArchitecture : SpectralResult = await runSpectralValidations ( jsonSchemaArchitecture , validationRulesForArchitecture ) ;
236+
237+ let jsonSchemaValidations = [ ] ;
238+ let errors = spectralResultForArchitecture . errors ;
239+ const warnings = spectralResultForArchitecture . warnings ;
240+
241+ logger . debug ( `metaSchemaPath provided: ${ metaSchemaPath } ` ) ;
242+
243+ // If metaSchemaPath is provided, attempt to validate against the CALM schema specified in the architecture
244+ if ( metaSchemaPath ) {
245+ logger . debug ( 'Attempting CALM schema validation' ) ;
246+ try {
247+ const architectureObj = typeof jsonSchemaArchitecture === 'string' ? JSON . parse ( jsonSchemaArchitecture ) : jsonSchemaArchitecture ;
248+ const schemaUrl = architectureObj . $schema ;
249+
250+ logger . debug ( `Parsed architecture object, $schema: ${ schemaUrl } ` ) ;
251+
252+ if ( schemaUrl ) {
253+ logger . debug ( `Found $schema reference: ${ schemaUrl } ` ) ;
254+ logger . debug ( 'Validating architecture against its specified CALM schema' ) ;
255+
256+ const schemaDirectory = await loadMetaSchemas ( metaSchemaPath , debug ) ;
257+ const ajv = buildAjv2020 ( schemaDirectory , debug ) ;
258+
259+ // Load the schema from the URL specified in the architecture
260+ logger . debug ( `Loading schema from: ${ schemaUrl } ` ) ;
261+
262+ // For schema loading, we need to handle both URL and local file cases
263+ let calmSchemaObject ;
264+ const urlPattern = / ^ h t t p s ? : \/ \/ / ;
265+ if ( urlPattern . test ( schemaUrl ) ) {
266+ const content = await loadFileFromUrl ( schemaUrl ) ;
267+ calmSchemaObject = typeof content === 'string' ? JSON . parse ( content ) : content ;
268+ } else {
269+ // For local files, read as raw string and parse
270+ if ( ! existsSync ( schemaUrl ) ) {
271+ throw new Error ( `Schema file could not be found at ${ schemaUrl } ` ) ;
272+ }
273+ const rawContent = await fs . readFile ( schemaUrl , 'utf-8' ) ;
274+ calmSchemaObject = JSON . parse ( rawContent ) ;
275+ }
276+
277+ logger . debug ( 'Loaded schema object' ) ;
278+
279+ const validateSchema = await ajv . compileAsync ( calmSchemaObject ) ;
280+
281+ logger . debug ( 'Compiled schema, running validation' ) ;
282+ const validationResult = validateSchema ( architectureObj ) ;
283+ logger . debug ( `Validation result: ${ validationResult } ` ) ;
284+
285+ if ( ! validationResult ) {
286+ logger . debug ( `JSON Schema validation raw output: ${ prettifyJson ( validateSchema . errors ) } ` ) ;
287+ errors = true ;
288+ jsonSchemaValidations = convertJsonSchemaIssuesToValidationOutputs ( validateSchema . errors ) ;
289+ logger . debug ( `Converted ${ jsonSchemaValidations . length } validation errors` ) ;
290+ }
291+ } else {
292+ logger . debug ( 'No $schema property found in architecture document, skipping CALM schema validation' ) ;
293+ }
294+ } catch ( error ) {
295+ logger . debug ( `Error during CALM schema validation: ${ error . message } ` ) ;
296+ logger . debug ( `Error stack: ${ error . stack } ` ) ;
297+ // Don't fail the entire validation if schema validation fails - just log and continue with spectral validation
298+ }
299+ } else {
300+ logger . debug ( 'No metaSchemaPath provided, skipping CALM schema validation' ) ;
301+ }
302+
303+ logger . debug ( `Returning validation outcome with ${ jsonSchemaValidations . length } JSON schema validations, errors: ${ errors } ` ) ;
304+ return new ValidationOutcome ( jsonSchemaValidations , spectralResultForArchitecture . spectralIssues , errors , warnings ) ;
234305}
235306
236307function extractSpectralRuleNames ( ) : string [ ] {
0 commit comments