Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add component.yaml v1.1 configuration section validations #25

Merged
merged 4 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,31 @@ yup.addMethod(yup.array, "checkEndpointNameUniqueness", function () {
});
});

// check envVariableUniqueness - Custom validation method to check if env variable names are unique
yup.addMethod(yup.array, "checkEnvVariableUniqueness", function () {
return this.test({
name: "unique-env-variable-name",
test: (arr) => {
// the env section is optional, hence return true if it is not present
if (!arr) {
return true;
}
const envSet = new Set();
const isUnique = arr.every((env) => {
envName = env.name;
if (envSet.has(envName)) {
return false;
}
envSet.add(envName);
return true;
});
return (
isUnique || new yup.ValidationError("Environment variable names must be unique")
);
},
});
});

// contextRequired - Custom validation method to check context is required for REST, GraphQL, and WS endpoints
yup.addMethod(yup.string, "contextRequired", function () {
return this.test({
Expand Down Expand Up @@ -292,6 +317,37 @@ const dependencySchemaV0D2 = yup.object().shape({
connectionReferences: connectionReferencesSchema,
});

const connectionRefSchema = yup.object().shape({
name: yup.string().required(),
key: yup.string().required(),
}).nullable().default(null);

const configGroupRefSchema = yup.object().shape({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we need two objects for the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the schemas are identical. However, connection refs and config group refs should be treated separately since there could be independent validations for either of them in coming designs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

name: yup.string().required(),
key: yup.string().required(),
}).nullable().default(null);

const envVariableSchema = yup.object().shape({
name: yup.string().required(),
value: yup.string(),
valueFrom: yup
.object()
.shape({
connectionRef: connectionRefSchema,
configGroupRef: configGroupRefSchema,
}),
}).test(
"oneOfRequired",
"One of value, connectionRef or configGroupRef must be provided",
function (envVariable) {
return envVariable?.value || envVariable?.valueFrom?.configGroupRef || envVariable?.valueFrom?.connectionRef;
}
);

const configurationSchema = yup.object().shape({
env: yup.array().of(envVariableSchema).checkEnvVariableUniqueness(),
});

// specSchema - Schema for spec definition
const specSchema = (srcDir) =>
yup.object().shape({
Expand Down Expand Up @@ -319,6 +375,7 @@ const componentYamlSchemaV1D1 = (srcDir) =>
.oneOf([1.1], 'Schema version must be 1.1'),
endpoints: endpointSchemaV0D2(srcDir),
dependencies: dependencySchemaV0D2,
configuration: configurationSchema,
});

// endpointYamlSchema - Schema for endpoints.yaml
Expand Down
69 changes: 69 additions & 0 deletions test/component-yaml-samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,42 @@ dependencies:
- from: ServiceURL
to: SERVICE_URL`;

const validComponentYamlV1D1 = `schemaVersion: 1.1
endpoints:
- name: greeter-sample
displayName: Go Greeter Sample
service:
basePath: /greeting-service
port: 9090
type: REST
networkVisibilities:
- Public
- Organization
schemaFilePath: dummy-openapi.yaml
dependencies:
connectionReferences:
- name: hello-conn
resourceRef: service:/connkeysrotation/hello-svc-4-12-2024/v1/803f0/PUBLIC
configuration:
env:
- name: HELLO_SERVICE_URL
valueFrom:
connectionRef:
name: hello-conn
key: ServiceURL
- name: HELLO_SERVICE_API_KEY
valueFrom:
connectionRef:
name: hello-conn
key: ChoreoAPIKey
- name: CONFIG_GRP_VAR
valueFrom:
configGroupRef:
name: hello-config-group
key: config-key
- name: CUSTOM_VAR
value: custom-value`;

const missingRequiredFieldsComponentYaml = `
endpoints:
- displayName: Go Greeter Sample`;
Expand Down Expand Up @@ -365,6 +401,37 @@ dependencies:
- name: valid_connection_name1
resourceRef: THIRDPARTY:mySqlDbServer/hotelDb/invalid`

const validateConfigurations = `schemaVersion: 1.1
configuration:
env:
- name: HELLO_SERVICE_URL
valueFrom:
connectionRef:
name: hello-conn
- name: HELLO_SERVICE_API_KEY
valueFrom:
connectionRef:
name: hello-conn
key: ChoreoAPIKey
- name: HELLO_SERVICE_API_KEY
valueFrom:
connectionRef:
name: hello-conn
key: ConsumerSecret
- name: HELLO_SERVICE_CONSUMER_KEY
valueFrom:
name: hello-conn
key: ConsumerKey
- name: HELLO_SERVICE_TOKEN_URL
valueFrom:
connectionRef:
key: TokenURL
- name: CONFIG_GRP_VAR
valueFrom:
configGroupRef:
name: hello-conn
- name: CUSTOM_VAR`

module.exports = {
validComponentYaml,
missingRequiredFieldsComponentYaml,
Expand All @@ -379,4 +446,6 @@ module.exports = {
validateServiceReferenceEnv,
validateConnectionReferenceName,
validateConnectionReferenceResourceRef,
validateConfigurations,
validComponentYamlV1D1,
};
28 changes: 28 additions & 0 deletions test/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const {
validateServiceReferenceEnv,
validateConnectionReferenceName,
validateConnectionReferenceResourceRef,
validateConfigurations,
validComponentYamlV1D1,
} = require("./component-yaml-samples.js");

const testSrcDir = "test/";
Expand Down Expand Up @@ -355,3 +357,29 @@ describe("dependencySchemaV0D2 schema tests", () => {
);
});
})

describe("componentYamlSchemaV1D1 schema tests", () => {
test("should validate correctly with valid component.yaml v1.1", async () => {
const result = await validateComponentYamlSchema(
validComponentYamlV1D1,
ALLOWED_COMPONENT_YAML_VERSIONS[1]
);
expect(result).toBeDefined();
});
test("should fail when configuration is not valid", async () => {
const expectedErrors = [
"configuration.env[0].valueFrom.connectionRef.key is a required field",
"One of value, connectionRef or configGroupRef must be provided",
"configuration.env[4].valueFrom.connectionRef.name is a required field",
"configuration.env[5].valueFrom.configGroupRef.key is a required field",
"One of value, connectionRef or configGroupRef must be provided",
"Environment variable names must be unique",
];
await expectValidationErrors(
COMPONENT_YAML,
validateConfigurations,
expectedErrors,
ALLOWED_COMPONENT_YAML_VERSIONS[1]
);
});
});
Loading