Skip to content

Commit

Permalink
[Scala] Fix naming conflicts and update API references (#402)
Browse files Browse the repository at this point in the history
* [Scala] Fix naming conflicts and update API references

Fixes #357

* Convert generator to use TypeScript

* Add unit tests for emitting package declaration

* Move test into separate group

* Emit js.UndefOr for optional input object types

* Fix parameter section generation

* Update input object test snapshot
  • Loading branch information
shadaj authored Jun 11, 2018
1 parent 7723fcd commit 58f07c4
Show file tree
Hide file tree
Showing 13 changed files with 475 additions and 390 deletions.
2 changes: 1 addition & 1 deletion src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function generate(
output = generateFlowSource(context);
break;
case 'scala':
output = generateScalaSource(context, options);
output = generateScalaSource(context);
}

if (outputPath) {
Expand Down
118 changes: 59 additions & 59 deletions src/scala/codeGeneration.js → src/scala/codeGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,21 @@ import {
GraphQLError,
getNamedType,
isCompositeType,
isAbstractType,
isEqualType,
GraphQLScalarType,
GraphQLEnumType,
GraphQLList,
GraphQLNonNull,
GraphQLID,
GraphQLInputObjectType
} from 'graphql'

import { isTypeProperSuperTypeOf } from '../utilities/graphql'

import {
join,
wrap,
} from '../utilities/printing';

import {
packageDeclaration,
objectDeclaration,
caseClassDeclaration,
propertyDeclaration,
propertyDeclarations,
escapeIdentifierIfNeeded,
comment
} from './language';
Expand All @@ -35,16 +27,12 @@ import {
caseClassNameForInlineFragment,
operationClassName,
enumCaseName,
propertiesFromSelectionSet,
propertyFromField,
propertyFromInlineFragment,
propertyFromFragmentSpread
propertyFromLegacyField,
propertyFromInputField,
} from './naming';

import {
escapedString,
multilineString,
dictionaryLiteralForFieldArguments,
} from './values';

import {
Expand All @@ -53,15 +41,19 @@ import {
} from './types';

import CodeGenerator from '../utilities/CodeGenerator';
import { LegacyCompilerContext, LegacyOperation, LegacyFragment, LegacyField, LegacyInlineFragment } from '../compiler/legacyIR';
import { GraphQLType } from 'graphql';
import { Property } from './language';
import { GraphQLCompositeType } from 'graphql';

export function generateSource(context, options) {
export function generateSource(context: LegacyCompilerContext) {
const generator = new CodeGenerator(context);

generator.printOnNewline('// This file was automatically generated and should not be edited.');
generator.printNewline();

if (context.namespace) {
packageDeclaration(generator, context.namespace);
if (context.options.namespace) {
packageDeclaration(generator, context.options.namespace);
}

context.typesUsed.forEach(type => {
Expand All @@ -80,7 +72,7 @@ export function generateSource(context, options) {
}

export function classDeclarationForOperation(
generator,
generator: CodeGenerator<LegacyCompilerContext, any>,
{
operationName,
operationType,
Expand All @@ -91,9 +83,8 @@ export function classDeclarationForOperation(
fragmentSpreads,
fragmentsReferenced,
source,
sourceWithFragments,
operationId
}
}: LegacyOperation
) {
let objectName;
let protocol;
Expand All @@ -113,8 +104,7 @@ export function classDeclarationForOperation(

objectDeclaration(generator, {
objectName,
modifiers: [],
superclass: protocol
superclass: protocol,
}, () => {
if (source) {
generator.printOnNewline('val operationString =');
Expand All @@ -123,7 +113,9 @@ export function classDeclarationForOperation(
});
}

operationIdentifier(generator, { operationName, sourceWithFragments, operationId });
if (operationId) {
operationIdentifier(generator, operationId);
}

if (fragmentsReferenced && fragmentsReferenced.length > 0) {
generator.printNewlineIfNeeded();
Expand All @@ -144,7 +136,7 @@ export function classDeclarationForOperation(
const properties = variables.map(({ name, type }) => {
const propertyName = escapeIdentifierIfNeeded(name);
const typeName = typeNameFromGraphQLType(generator.context, type);
const isOptional = !(type instanceof GraphQLNonNull || type.ofType instanceof GraphQLNonNull);
const isOptional = !(type instanceof GraphQLNonNull || type instanceof GraphQLNonNull);
return { name, propertyName, type, typeName, isOptional };
});

Expand Down Expand Up @@ -172,15 +164,15 @@ export function classDeclarationForOperation(
}

export function caseClassDeclarationForFragment(
generator,
generator: CodeGenerator<LegacyCompilerContext, any>,
{
fragmentName,
typeCondition,
fields,
inlineFragments,
fragmentSpreads,
source
}
}: LegacyFragment
) {
const caseClassName = caseClassNameForFragmentName(fragmentName);

Expand All @@ -190,7 +182,7 @@ export function caseClassDeclarationForFragment(
fields,
inlineFragments,
fragmentSpreads
}, () => {}, () => {
}, () => {
if (source) {
generator.printOnNewline('val fragmentString =');
generator.withIndent(() => {
Expand All @@ -201,25 +193,32 @@ export function caseClassDeclarationForFragment(
}

export function caseClassDeclarationForSelectionSet(
generator,
generator: CodeGenerator<LegacyCompilerContext, any>,
{
caseClassName,
parentType,
fields,
inlineFragments,
fragmentSpreads,
viewableAs
}: {
caseClassName: string,
parentType: GraphQLCompositeType,
fields: LegacyField[],
inlineFragments?: LegacyInlineFragment[],
fragmentSpreads?: string[],
viewableAs?: {
caseClassName: string,
properties: (LegacyField & Property)[]
}
},
beforeClosure,
objectClosure,
objectClosure?: () => void,
) {
const possibleTypes = parentType ? possibleTypesForType(generator.context, parentType) : null;

let properties;

if (!possibleTypes || possibleTypes.length == 1) {
properties = fields
.map(field => propertyFromField(generator.context, field))
const properties = fields
.map(field => propertyFromLegacyField(generator.context, field, caseClassName))
.filter(field => field.propertyName != "__typename");

caseClassDeclaration(generator, { caseClassName, params: properties.map(p => {
Expand All @@ -231,22 +230,22 @@ export function caseClassDeclarationForSelectionSet(
} else {
generator.printNewlineIfNeeded();
const properties = fields
.map(field => propertyFromField(generator.context, field))
.map(field => propertyFromLegacyField(generator.context, field, caseClassName))
.filter(field => field.propertyName != "__typename");

caseClassDeclaration(generator, { caseClassName, params: properties.map(p => {
return {
name: p.responseName,
type: p.typeName,
};
}), superclass: 'me.shadaj.slinky.core.WithRaw'}, () => {
}), superclass: 'slinky.readwrite.WithRaw'}, () => {
if (inlineFragments && inlineFragments.length > 0) {
inlineFragments.forEach((inlineFragment) => {
const fragClass = caseClassNameForInlineFragment(inlineFragment);
generator.printOnNewline(`def as${inlineFragment.typeCondition}`);
generator.print(`: Option[${fragClass}] =`);
generator.withinBlock(() => {
generator.printOnNewline(`if (${fragClass}.possibleTypes.contains(this.raw.__typename.asInstanceOf[String])) Some(implicitly[me.shadaj.slinky.core.Reader[${fragClass}]].read(this.raw)) else None`);
generator.printOnNewline(`if (${fragClass}.possibleTypes.contains(this.raw.__typename.asInstanceOf[String])) Some(implicitly[slinky.readwrite.Reader[${fragClass}]].read(this.raw)) else None`);
});
});
}
Expand All @@ -259,7 +258,7 @@ export function caseClassDeclarationForSelectionSet(
generator.printOnNewline(`def as${s}`);
generator.print(`: Option[${s}] =`);
generator.withinBlock(() => {
generator.printOnNewline(`if (${s}.possibleTypes.contains(this.raw.__typename.asInstanceOf[String])) Some(implicitly[me.shadaj.slinky.core.Reader[${s}]].read(this.raw)) else None`);
generator.printOnNewline(`if (${s}.possibleTypes.contains(this.raw.__typename.asInstanceOf[String])) Some(implicitly[slinky.readwrite.Reader[${s}]].read(this.raw)) else None`);
});
}
})
Expand All @@ -275,7 +274,7 @@ export function caseClassDeclarationForSelectionSet(
caseClassName: caseClassNameForInlineFragment(inlineFragment),
parentType: inlineFragment.typeCondition,
fields: inlineFragment.fields,
inlineFragments: inlineFragment.inlineFragments,
// inlineFragments: inlineFragment.inlineFragments,
fragmentSpreads: inlineFragment.fragmentSpreads,
viewableAs: {
caseClassName,
Expand Down Expand Up @@ -309,43 +308,43 @@ export function caseClassDeclarationForSelectionSet(
})
}

fields.filter(field => isCompositeType(getNamedType(field.type))).forEach(field => {
caseClassDeclarationForSelectionSet(
generator,
{
caseClassName: caseClassNameForPropertyName(field.responseName),
parentType: getNamedType(field.type) as GraphQLCompositeType,
fields: field.fields || [],
inlineFragments: field.inlineFragments,
fragmentSpreads: field.fragmentSpreads
}
);
});

if (objectClosure) {
objectClosure();
}
});

fields.filter(field => isCompositeType(getNamedType(field.type))).forEach(field => {
caseClassDeclarationForSelectionSet(
generator,
{
caseClassName: caseClassNameForPropertyName(field.responseName),
parentType: getNamedType(field.type),
fields: field.fields,
inlineFragments: field.inlineFragments,
fragmentSpreads: field.fragmentSpreads
}
);
});
}

function operationIdentifier(generator, { operationName, sourceWithFragments, operationId }) {
if (!generator.context.generateOperationIds) {
function operationIdentifier(generator: CodeGenerator<LegacyCompilerContext, any>, operationId: string) {
if (!generator.context.options.generateOperationIds) {
return
}

generator.printNewlineIfNeeded();
generator.printOnNewline(`val operationIdentifier: String = "${operationId}"`);
}

export function typeDeclarationForGraphQLType(generator, type) {
export function typeDeclarationForGraphQLType(generator: CodeGenerator<LegacyCompilerContext, any>, type: GraphQLType) {
if (type instanceof GraphQLEnumType) {
enumerationDeclaration(generator, type);
} else if (type instanceof GraphQLInputObjectType) {
caseClassDeclarationForInputObjectType(generator, type);
}
}

function enumerationDeclaration(generator, type) {
function enumerationDeclaration(generator: CodeGenerator<LegacyCompilerContext, any>, type: GraphQLEnumType) {
const { name, description } = type;
const values = type.getValues();

Expand All @@ -361,15 +360,16 @@ function enumerationDeclaration(generator, type) {
generator.printNewline();
}

function caseClassDeclarationForInputObjectType(generator, type) {
function caseClassDeclarationForInputObjectType(generator: CodeGenerator<LegacyCompilerContext, any>, type: GraphQLInputObjectType) {
const { name: caseClassName, description } = type;
const fields = Object.values(type.getFields());
const properties = fields.map(field => propertyFromField(generator.context, field));
const properties = fields.map(field => propertyFromInputField(generator.context, field, generator.context.options.namespace));

caseClassDeclaration(generator, { caseClassName, description, params: properties.map(p => {
return {
name: p.propertyName,
type: p.typeName
type: p.typeName,
defaultValue: p.isOptional ? "scala.scalajs.js.undefined" : ""
};
})}, () => {});
}
File renamed without changes.
66 changes: 0 additions & 66 deletions src/scala/language.js

This file was deleted.

Loading

0 comments on commit 58f07c4

Please sign in to comment.