Skip to content

Commit

Permalink
Merge branch 'master' into ag_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored Dec 31, 2023
2 parents 59c54c5 + 4d9a55c commit bfb5f0c
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 4 deletions.
12 changes: 11 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@
"bug",
"example",
"doc",
"test"
"test",
"maintenance"
]
},
{
Expand Down Expand Up @@ -826,6 +827,15 @@
"contributions": [
"doc"
]
},
{
"login": "ankur0904",
"name": "Ankur Singh",
"avatar_url": "https://avatars.githubusercontent.com/u/98346896?v=4",
"profile": "https://github.com/ankur0904",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 7,
Expand Down
3 changes: 3 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@

# Language Champions for PHP and it's presets
*/generators/php @ferror

# Language Champions for Scala and it's presets
*/generators/scala @artur-ciocanu
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[![Discussions](https://img.shields.io/github/discussions/asyncapi/modelina)](https://github.com/asyncapi/modelina/discussions)
[![Website](https://img.shields.io/website?label=website&url=https%3A%2F%2Fwww.modelina.org)](https://www.modelina.org)
[![Playground](https://img.shields.io/website?label=playground&url=https%3A%2F%2Fwww.modelina.org%2Fplayground)](https://www.modelina.org/playground) <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-76-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-77-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->

Your one-stop tool for generating accurate and well-tested models for representing the message payloads. Use it as a tool in your development workflow, or a library in a larger integrations, entirely in your control.
Expand Down Expand Up @@ -361,7 +361,7 @@ Thanks go out to these wonderful people ([emoji key](https://allcontributors.org
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PanMan"><img src="https://avatars.githubusercontent.com/u/978501?v=4?s=100" width="100px;" alt="PanMan"/><br /><sub><b>PanMan</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=PanMan" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/artur-ciocanu"><img src="https://avatars.githubusercontent.com/u/743192?v=4?s=100" width="100px;" alt="artur-ciocanu"/><br /><sub><b>artur-ciocanu</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Code">💻</a> <a href="https://github.com/asyncapi/modelina/issues?q=author%3Aartur-ciocanu" title="Bug reports">🐛</a> <a href="#example-artur-ciocanu" title="Examples">💡</a> <a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Documentation">📖</a> <a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/artur-ciocanu"><img src="https://avatars.githubusercontent.com/u/743192?v=4?s=100" width="100px;" alt="artur-ciocanu"/><br /><sub><b>artur-ciocanu</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Code">💻</a> <a href="https://github.com/asyncapi/modelina/issues?q=author%3Aartur-ciocanu" title="Bug reports">🐛</a> <a href="#example-artur-ciocanu" title="Examples">💡</a> <a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Documentation">📖</a> <a href="https://github.com/asyncapi/modelina/commits?author=artur-ciocanu" title="Tests">⚠️</a> <a href="#maintenance-artur-ciocanu" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://micro-jumbo.eu/"><img src="https://avatars.githubusercontent.com/u/11511697?v=4?s=100" width="100px;" alt="Cyprian Gracz"/><br /><sub><b>Cyprian Gracz</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=micro-jumbo" title="Code">💻</a> <a href="https://github.com/asyncapi/modelina/commits?author=micro-jumbo" title="Tests">⚠️</a> <a href="https://github.com/asyncapi/modelina/issues?q=author%3Amicro-jumbo" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.printnanny.ai"><img src="https://avatars.githubusercontent.com/u/2601819?v=4?s=100" width="100px;" alt="Leigh Johnson"/><br /><sub><b>Leigh Johnson</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=leigh-johnson" title="Code">💻</a> <a href="https://github.com/asyncapi/modelina/commits?author=leigh-johnson" title="Tests">⚠️</a> <a href="#example-leigh-johnson" title="Examples">💡</a> <a href="https://github.com/asyncapi/modelina/commits?author=leigh-johnson" title="Documentation">📖</a> <a href="#maintenance-leigh-johnson" title="Maintenance">🚧</a> <a href="https://github.com/asyncapi/modelina/pulls?q=is%3Apr+reviewed-by%3Aleigh-johnson" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nitintejuja"><img src="https://avatars.githubusercontent.com/u/95347924?v=4?s=100" width="100px;" alt="Nitin Tejuja"/><br /><sub><b>Nitin Tejuja</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=nitintejuja" title="Tests">⚠️</a> <a href="#example-nitintejuja" title="Examples">💡</a></td>
Expand Down Expand Up @@ -420,6 +420,7 @@ Thanks go out to these wonderful people ([emoji key](https://allcontributors.org
<td align="center" valign="top" width="14.28%"><a href="https://mintu-portfolio.netlify.app/"><img src="https://avatars.githubusercontent.com/u/127925465?v=4?s=100" width="100px;" alt="Mintu Gogoi"/><br /><sub><b>Mintu Gogoi</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=Min2who" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/devansh-m12"><img src="https://avatars.githubusercontent.com/u/86195162?v=4?s=100" width="100px;" alt="Devansh Mahant"/><br /><sub><b>Devansh Mahant</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=devansh-m12" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/officialasishkumar"><img src="https://avatars.githubusercontent.com/u/87874775?v=4?s=100" width="100px;" alt="Asish Kumar"/><br /><sub><b>Asish Kumar</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=officialasishkumar" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ankur0904"><img src="https://avatars.githubusercontent.com/u/98346896?v=4?s=100" width="100px;" alt="Ankur Singh"/><br /><sub><b>Ankur Singh</b></sub></a><br /><a href="https://github.com/asyncapi/modelina/commits?author=ankur0904" title="Documentation">📖</a></td>
</tr>
</tbody>
</table>
Expand Down
41 changes: 41 additions & 0 deletions docs/constraints/Cplusplus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# C++ Constraints

These are the C++ specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel.

## Model Naming
These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](./README.md#Customization).

|Rule key|Rule|Resolution|
|---|---|---|
|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For C++ `_` are an exception to this rule. |
|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character|
|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. |
|NO_RESERVED_KEYWORDS|No reserved keywords|C++ has a list of reserved keywords ([see the full list here](../../src/generators/cplusplus/Constants.ts))|
|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using snake case|

## Property naming
These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](./README.md#Customization).
|Rule key|Rule|Resolution|
|---|---|---|
|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For C++ `_` are an exception to this rule. |
|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character|
|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. |
|NO_RESERVED_KEYWORDS|No reserved keywords|C++ has a list of reserved keywords ([see the full list here](../../src/generators/cplusplus/Constants.ts))|
|NAMING_FORMATTER|Must be formatted equally|Property naming is formatted using snake case|
|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.|


## Enum key constraints
These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](./README.md#Customization).

|Rule key|Rule|Resolution|
|---|---|---|
|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For C++ `_` are an exception to this rule. |
|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character|
|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. |
|NO_RESERVED_KEYWORDS|No reserved keywords|C++ has a list of reserved keywords ([see the full list here](../../src/generators/cplusplus/Constants.ts))|
|NAMING_FORMATTER|Must be formatted equally|Enum key is formatted using snake case|
|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.|

## Constant
These are the constraints that are applied to constants. Currently, there are no hooks one can overwrite inside it.
2 changes: 1 addition & 1 deletion modelina-website/src/components/layouts/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function Footer() {
</li>
<li className="py-2">
<a
href="https://asyncapi.threadless.com"
href="https://www.store.asyncapi.com/collections/all-products"
target="_blank"
rel="noopener noreferrer"
className="text-base leading-6 text-gray-500 hover:text-white transition ease-in-out duration-300"
Expand Down
19 changes: 19 additions & 0 deletions test/generators/php/constrainer/ConstantConstrainer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defaultConstantConstraints } from '../../../../src/generators/php/constrainer/ConstantConstrainer';

const mockConstantContext = {
constrainedMetaModel: {} as any
};

describe('defaultConstantConstraints', () => {
it('should return a function that returns undefined', () => {
const constantConstraintsFunction = defaultConstantConstraints();
const result = constantConstraintsFunction(mockConstantContext);
expect(result).toBeUndefined();
});

it('should return a ConstantConstraint type', () => {
const constantConstraintsFunction = defaultConstantConstraints();
const result = constantConstraintsFunction;
expect(typeof result).toBe('function');
});
});
167 changes: 167 additions & 0 deletions test/generators/php/constrainer/EnumConstrainer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { PhpDefaultConstraints } from '../../../../src/generators/php/PhpConstrainer';
import { EnumModel } from '../../../../src/models/MetaModel';
import {
ConstrainedEnumModel,
ConstrainedEnumValueModel
} from '../../../../src';
import {
defaultEnumKeyConstraints,
ModelEnumKeyConstraints
} from '../../../../src/generators/php/constrainer/EnumConstrainer';

describe('Php EnumConstrainer', () => {
const enumModel = new EnumModel('test', undefined, {}, []);
const constrainedEnumModel = new ConstrainedEnumModel(
'test',
undefined,
{},
'',
[]
);

describe('enum keys', () => {
test('should never render special chars', () => {
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: '%'
});
expect(constrainedKey).toEqual('PERCENT');
});

test('should not render number as start char', () => {
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: '1'
});
expect(constrainedKey).toEqual('NUMBER_1');
});

test('should not contain duplicate keys', () => {
const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel(
'EMPTY',
'return',
'return'
);
const constrainedEnumModel = new ConstrainedEnumModel(
'test',
undefined,
{},
'',
[existingConstrainedEnumValueModel]
);
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: ''
});
expect(constrainedKey).toEqual('RESERVED_EMPTY');
});

test('should never contain empty keys', () => {
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: ''
});
expect(constrainedKey).toEqual('RESERVED_EMPTY');
});

test('should use constant naming format', () => {
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: 'some weird_value!"#2'
});
expect(constrainedKey).toEqual(
'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2'
);
});

test('should never render reserved keywords', () => {
const constrainedKey = PhpDefaultConstraints.enumKey({
enumModel,
constrainedEnumModel,
enumKey: 'return'
});
expect(constrainedKey).toEqual('RESERVED_RETURN');
});
});

describe('enum values', () => {
test('should render string values', () => {
const constrainedValue = PhpDefaultConstraints.enumValue({
enumModel,
constrainedEnumModel,
enumValue: 'string value'
});
expect(constrainedValue).toEqual('"string value"');
});
test('should render boolean values', () => {
const constrainedBooleanValue = PhpDefaultConstraints.enumValue({
enumModel,
constrainedEnumModel,
enumValue: true
});
expect(constrainedBooleanValue).toEqual('"true"');
});

test('should render numbers', () => {
const constrainedNumberValue = PhpDefaultConstraints.enumValue({
enumModel,
constrainedEnumModel,
enumValue: 123
});
expect(constrainedNumberValue).toEqual(123);
});

test('should render object values', () => {
const constrainedObjectValue = PhpDefaultConstraints.enumValue({
enumModel,
constrainedEnumModel,
enumValue: { test: 'test' }
});
expect(constrainedObjectValue).toEqual('"{\\"test\\":\\"test\\"}"');
});

test('should render unknown value', () => {
const constrainedUnknownValue = PhpDefaultConstraints.enumValue({
enumModel,
constrainedEnumModel,
enumValue: undefined
});
expect(constrainedUnknownValue).toEqual(`"undefined"`);
});
});

describe('custom constraints', () => {
test('should be able to overwrite all hooks for enum key', () => {
const mockedConstraintCallbacks: Partial<ModelEnumKeyConstraints> = {
NAMING_FORMATTER: jest.fn().mockReturnValue(''),
NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
};
const constrainFunction = defaultEnumKeyConstraints(
mockedConstraintCallbacks
);
constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' });
for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
expect(jestMockCallback).toHaveBeenCalled();
}
});
test('should be able to overwrite specific hooks for enum key', () => {
const mockedConstraintCallbacks: Partial<ModelEnumKeyConstraints> = {
NAMING_FORMATTER: jest.fn().mockReturnValue('')
};
const constrainFunction = defaultEnumKeyConstraints(
mockedConstraintCallbacks
);
constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' });

expect(mockedConstraintCallbacks.NAMING_FORMATTER).toHaveBeenCalled();
});
});
});
75 changes: 75 additions & 0 deletions test/generators/php/constrainer/ModelNameConstrainer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
DefaultModelNameConstraints,
defaultModelNameConstraints,
ModelNameConstraints
} from '../../../../src/generators/php/constrainer/ModelNameConstrainer';

describe('PHP ModelNameConstrainer', () => {
const PhpDefaultConstraints: ModelNameConstraints =
DefaultModelNameConstraints;

test('should replace special characters with underscores', () => {
const constrainedName = PhpDefaultConstraints.NO_SPECIAL_CHAR('name%$test');
expect(constrainedName).toEqual('name_percent_dollar_test');
});

test('should handle number start characters', () => {
const constrainedName = PhpDefaultConstraints.NO_NUMBER_START_CHAR('1Test');
expect(constrainedName).toEqual('number_1Test');
});

test('should handle empty value by default', () => {
const constrainedName = PhpDefaultConstraints.NO_EMPTY_VALUE('');
expect(constrainedName).toEqual('empty');
});

test('should convert to PascalCase by default', () => {
const constrainedName = PhpDefaultConstraints.NAMING_FORMATTER('test_name');
expect(constrainedName).toEqual('TestName');
});

test('should handle reserved PHP keywords', () => {
const constrainedName =
PhpDefaultConstraints.NO_RESERVED_KEYWORDS('return');
expect(constrainedName).toEqual('reserved_return');
});

describe('Custom PHP constraints', () => {
test('should be able to overwrite all hooks for PHP', () => {
const mockedConstraintCallbacks: Partial<ModelNameConstraints> = {
NAMING_FORMATTER: jest.fn().mockReturnValue(''),
NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
};
const constrainFunction = defaultModelNameConstraints(
mockedConstraintCallbacks
);
constrainFunction({ modelName: '' });
// Expect all callbacks to be called
for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
expect(jestMockCallback).toHaveBeenCalled();
}
});

test('should be able to overwrite one hook for PHP', () => {
// All but NAMING_FORMATTER, as we customize that
const spies = [
jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'),
jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'),
jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'),
jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS')
];
const jestCallback = jest.fn().mockReturnValue('');
const constrainFunction = defaultModelNameConstraints({
NAMING_FORMATTER: jestCallback
});
const constrainedValue = constrainFunction({ modelName: '' });
expect(constrainedValue).toEqual('');
for (const jestMockCallback of spies) {
expect(jestMockCallback).toHaveBeenCalled();
}
});
});
});
Loading

0 comments on commit bfb5f0c

Please sign in to comment.