Skip to content

Commit

Permalink
fix: "Malformed enum value" when using @Scoped packages (#139)
Browse files Browse the repository at this point in the history
Fixes #138

The code that validated the format of jsii enum values was too rigid
and didn't allow "/" in type names. When using a package from an npm
scope, the enum value will look like this `@scope/foo.EnumType/MemberName`.

Also, add a verification that the enum member exists in the enum and fail
otherwise.

Required fix in kernel, java and dotnet runtimes.
  • Loading branch information
Elad Ben-Israel authored Aug 6, 2018
1 parent 8416b6a commit 4e70209
Show file tree
Hide file tree
Showing 20 changed files with 348 additions and 18 deletions.
9 changes: 9 additions & 0 deletions packages/jsii-calc-lib/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,12 @@ export interface StructWithOnlyOptionals {
optional2?: number
optional3?: boolean
}

/**
* Check that enums from @scoped packages can be references.
* See awslabs/jsii#138
*/
export enum EnumFromScopedModule {
Value1,
Value2
}
20 changes: 19 additions & 1 deletion packages/jsii-calc-lib/test/assembly.jsii
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"fingerprint": "GbVnxjsdaq598NCRdOJ8entjQRwThRmFgdeLNaQbIZY=",
"fingerprint": "yzHGjyI4/53o87fPNjR/Nz/4G58kg5jVoKtgwdqaEU8=",
"author": {
"name": "Amazon Web Services",
"organization": true,
Expand Down Expand Up @@ -73,6 +73,24 @@
}
},
"types": {
"@scope/jsii-calc-lib.EnumFromScopedModule": {
"assembly": "@scope/jsii-calc-lib",
"docs": {
"comment": "Check that enums from @scoped packages can be references.\nSee awslabs/jsii#138"
},
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule",
"kind": "enum",
"members": [
{
"name": "Value1"
},
{
"name": "Value2"
}
],
"name": "EnumFromScopedModule",
"namespace": "@scope/jsii-calc-lib"
},
"@scope/jsii-calc-lib.IFriendly": {
"assembly": "@scope/jsii-calc-lib",
"docs": {
Expand Down
17 changes: 16 additions & 1 deletion packages/jsii-calc/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// tslint:disable
import { Operation, Value, Number, IFriendly, MyFirstStruct, StructWithOnlyOptionals } from '@scope/jsii-calc-lib'
import { Operation, Value, Number, IFriendly, MyFirstStruct, StructWithOnlyOptionals, EnumFromScopedModule } from '@scope/jsii-calc-lib'
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
Expand Down Expand Up @@ -1101,4 +1101,19 @@ export class UseCalcBase {

export interface ImplictBaseOfBase extends base.BaseProps {
goo: Date;
}

/**
* See awslabs/jsii#138
*/
export class ReferenceEnumFromScopedPackage {
public foo?: EnumFromScopedModule = EnumFromScopedModule.Value2;

public loadFoo(): EnumFromScopedModule | undefined {
return this.foo;
}

public saveFoo(value: EnumFromScopedModule) {
this.foo = value;
}
}
44 changes: 43 additions & 1 deletion packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"fingerprint": "ZR90w/OQ/vVxm0Cg1sicOKwbZxbGK43MXqj2ksUyCrk=",
"fingerprint": "ENUxKESaYuaH8SRuMSsikJscFgsj+E8Ey1WvHIsNsZg=",
"author": {
"name": "Amazon Web Services",
"organization": true,
Expand Down Expand Up @@ -1919,6 +1919,48 @@
}
]
},
"jsii-calc.ReferenceEnumFromScopedPackage": {
"assembly": "jsii-calc",
"docs": {
"comment": "See awslabs/jsii#138"
},
"fqn": "jsii-calc.ReferenceEnumFromScopedPackage",
"initializer": {
"initializer": true
},
"kind": "class",
"methods": [
{
"name": "loadFoo",
"returns": {
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule",
"optional": true
}
},
{
"name": "saveFoo",
"parameters": [
{
"name": "value",
"type": {
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule"
}
}
]
}
],
"name": "ReferenceEnumFromScopedPackage",
"namespace": "jsii-calc",
"properties": [
{
"name": "foo",
"type": {
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule",
"optional": true
}
}
]
},
"jsii-calc.ReturnsNumber": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.ReturnsNumber",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ public EnumValue(string value)
{
Value = value ?? throw new ArgumentNullException(nameof(value));

string[] valueTokens = value.Split('/');
if (valueTokens.Length != 2)
int sep = value.LastIndexOf('/');
if (sep == -1)
{
throw new ArgumentException($"Unexpected format for enum value: {value}", nameof(value));
}

FullyQualifiedName = valueTokens[0];
MemberName = valueTokens[1];
FullyQualifiedName = value.Substring(0, sep);
MemberName = value.Substring(sep + 1);
}

public EnumValue(string fullyQualifiedName, string memberName) : this($"{fullyQualifiedName}/{memberName}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,17 @@ public void GetAndSetEnumValues()
Assert.Equal("<<[[{{(((1 * (0 + 9)) * (0 + 9)) * (0 + 9))}}]]>>", calc.ToString());
}

[Fact(DisplayName = Prefix + nameof(EnumFromScopedModule))]
public void UseEnumFromScopedModule()
{
ReferenceEnumFromScopedPackage obj = new ReferenceEnumFromScopedPackage();
Assert.Equal(EnumFromScopedModule.Value2, obj.Foo);
obj.Foo = EnumFromScopedModule.Value1;
Assert.Equal(EnumFromScopedModule.Value1, obj.LoadFoo());
obj.SaveFoo(EnumFromScopedModule.Value2);
Assert.Equal(EnumFromScopedModule.Value2, obj.Foo);
}

[Fact(DisplayName = Prefix + nameof(UndefinedAndNull))]
public void UndefinedAndNull()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import software.amazon.jsii.tests.calculator.NumberGenerator;
import software.amazon.jsii.tests.calculator.Polymorphism;
import software.amazon.jsii.tests.calculator.Power;
import software.amazon.jsii.tests.calculator.ReferenceEnumFromScopedPackage;
import software.amazon.jsii.tests.calculator.Statics;
import software.amazon.jsii.tests.calculator.Sum;
import software.amazon.jsii.tests.calculator.SyncVirtualMethods;
import software.amazon.jsii.tests.calculator.UnionProperties;
import software.amazon.jsii.tests.calculator.UsesInterfaceWithProperties;
import software.amazon.jsii.tests.calculator.composition.CompositionStringStyle;
import software.amazon.jsii.tests.calculator.lib.EnumFromScopedModule;
import software.amazon.jsii.tests.calculator.lib.IFriendly;
import software.amazon.jsii.tests.calculator.lib.MyFirstStruct;
import software.amazon.jsii.tests.calculator.lib.Number;
Expand Down Expand Up @@ -242,6 +244,16 @@ public void getAndSetEnumValues() {
assertEquals("<<[[{{(((1 * (0 + 9)) * (0 + 9)) * (0 + 9))}}]]>>", calc.toString());
}

@Test
public void useEnumFromScopedModule() {
ReferenceEnumFromScopedPackage obj = new ReferenceEnumFromScopedPackage();
assertEquals(EnumFromScopedModule.Value2, obj.getFoo());
obj.setFoo(EnumFromScopedModule.Value1);
assertEquals(EnumFromScopedModule.Value1, obj.loadFoo());
obj.saveFoo(EnumFromScopedModule.Value2);
assertEquals(EnumFromScopedModule.Value2, obj.getFoo());
}

@Test
public void undefinedAndNull() {
Calculator calculator = new Calculator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,13 @@ private JsiiObject createNative(final String fqn) {
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Enum<?> findEnumValue(final String enumRef) {
String[] parts = enumRef.split("/");
if (parts.length != 2) {
int sep = enumRef.lastIndexOf('/');
if (sep == -1) {
throw new JsiiException("Malformed enum reference: " + enumRef);
}

String typeName = parts[0];
String valueName = parts[1];
String typeName = enumRef.substring(0, sep);
String valueName = enumRef.substring(sep + 1);

String enumClass = resolveJavaClassName(typeName);
try {
Expand Down
15 changes: 11 additions & 4 deletions packages/jsii-kernel/lib/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -855,14 +855,21 @@ export class Kernel {
// enums
if (typeof v === 'object' && TOKEN_ENUM in v) {
this._debug('Enum:', v);
const parts = v[TOKEN_ENUM].split('/');
if (parts.length !== 2) {

const value = v[TOKEN_ENUM] as string;
const sep = value.lastIndexOf('/');
if (sep === -1) {
throw new Error(`Malformed enum value: ${v[TOKEN_ENUM]}`);
}
const typeName = parts[0];
const valueName = parts[1];

const typeName = value.substr(0, sep);
const valueName = value.substr(sep + 1);

const enumValue = this._findSymbol(typeName)[valueName];
if (enumValue === undefined) {
throw new Error(`No enum member named ${valueName} in ${typeName}`);
}

this._debug('resolved enum value:', enumValue);
return enumValue;
}
Expand Down
23 changes: 23 additions & 0 deletions packages/jsii-kernel/test/test.kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,29 @@ defineTest('in/out enum values', async (test, sandbox) => {
test.deepEqual(sandbox.get({ objref: alltypes, property: 'enumProperty' }).value, { '$jsii.enum': 'jsii-calc.AllTypesEnum/ThisIsGreat' });
});

defineTest('enum values from @scoped packages awslabs/jsii#138', async (test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.ReferenceEnumFromScopedPackage' });

const value = sandbox.get({ objref, property: 'foo' });
test.deepEqual(value, { value: { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/Value2' } });

sandbox.set({ objref, property: 'foo', value: { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/Value1' }});
const ret = sandbox.invoke({ objref, method: 'loadFoo' });
test.deepEqual(ret, { result: { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/Value1' } });

sandbox.invoke({ objref, method: 'saveFoo', args: [ { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/Value2' } ] });
const value2 = sandbox.get({ objref, property: 'foo' });
test.deepEqual(value2, { value: { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/Value2' } });
});

defineTest('fails for invalid enum member name', async (test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.ReferenceEnumFromScopedPackage' });

test.throws(() => {
sandbox.set({ objref, property: 'foo', value: { '$jsii.enum': '@scope/jsii-calc-lib.EnumFromScopedModule/ValueX' }});
}, /No enum member named ValueX/);
});

defineTest('set for a non existing property', async (test, sandbox) => {
const obj = sandbox.create({ fqn: 'jsii-calc.SyncVirtualMethods' });
test.throws(() => sandbox.set({ objref: obj, property: 'idontexist', value: 'Foo' }));
Expand Down
2 changes: 2 additions & 0 deletions packages/jsii-pacmak/bin/jsii-pacmak.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ import { SPEC_FILE_NAME } from '../node_modules/jsii-spec';
return; // already built
}

visited.add(packageDir);

// read package.json and extract the "jsii" configuration from it.
const pkg = await fs.readJson(path.join(packageDir, 'package.json'));
if (!pkg.jsii || !pkg.jsii.outdir || !pkg.jsii.targets) {
Expand Down
2 changes: 1 addition & 1 deletion packages/jsii-pacmak/test/diff-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function assert-generator() {
echo "The diff-test.sh harness will replace it with the real expected tarball" >> ${tarball_placeholder}
done

if ! diff -arq ${outdir} ${expected}; then
if ! diff --strip-trailing-cr -arq ${outdir} ${expected}; then
echo
echo "------------------------------------------------------------------------"
echo " diff-test for pacmak generator ${module} failed"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"fingerprint": "GbVnxjsdaq598NCRdOJ8entjQRwThRmFgdeLNaQbIZY=",
"fingerprint": "yzHGjyI4/53o87fPNjR/Nz/4G58kg5jVoKtgwdqaEU8=",
"author": {
"name": "Amazon Web Services",
"organization": true,
Expand Down Expand Up @@ -73,6 +73,24 @@
}
},
"types": {
"@scope/jsii-calc-lib.EnumFromScopedModule": {
"assembly": "@scope/jsii-calc-lib",
"docs": {
"comment": "Check that enums from @scoped packages can be references.\nSee awslabs/jsii#138"
},
"fqn": "@scope/jsii-calc-lib.EnumFromScopedModule",
"kind": "enum",
"members": [
{
"name": "Value1"
},
{
"name": "Value2"
}
],
"name": "EnumFromScopedModule",
"namespace": "@scope/jsii-calc-lib"
},
"@scope/jsii-calc-lib.IFriendly": {
"assembly": "@scope/jsii-calc-lib",
"docs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Tests.Calculator.Lib
{
/// <summary>
/// Check that enums from @scoped packages can be references.
/// See awslabs/jsii#138
/// </summary>
[JsiiEnum(typeof(EnumFromScopedModule), "@scope/jsii-calc-lib.EnumFromScopedModule")]
public enum EnumFromScopedModule
{
[JsiiEnumMember("Value1")]
Value1,
[JsiiEnumMember("Value2")]
Value2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package software.amazon.jsii.tests.calculator.lib;
/**
* Check that enums from @scoped packages can be references.
* See awslabs/jsii#138
*/
@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.lib.$Module.class, fqn = "@scope/jsii-calc-lib.EnumFromScopedModule")
public enum EnumFromScopedModule {
Value1,
Value2,
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Reference

.. py:module:: @scope/jsii-calc-lib
EnumFromScopedModule (enum)
^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. py:class:: EnumFromScopedModule
.. py:data:: Value1
.. py:data:: Value2
IFriendly (interface)
^^^^^^^^^^^^^^^^^^^^^

Expand Down
Loading

0 comments on commit 4e70209

Please sign in to comment.