@aws-amplify/appsync-modelgen-plugin
Version:
827 lines (824 loc) • 45.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppSyncModelJavaVisitor = void 0;
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const change_case_1 = require("change-case");
const ts_dedent_1 = __importDefault(require("ts-dedent"));
const java_config_1 = require("../configs/java-config");
const scalars_1 = require("../scalars");
const java_declaration_block_1 = require("../languages/java-declaration-block");
const appsync_visitor_1 = require("./appsync-visitor");
const process_connections_1 = require("../utils/process-connections");
const process_auth_1 = require("../utils/process-auth");
const warn_1 = require("../utils/warn");
const validate_field_name_1 = require("../utils/validate-field-name");
class AppSyncModelJavaVisitor extends appsync_visitor_1.AppSyncModelVisitor {
constructor() {
super(...arguments);
this.additionalPackages = new Set();
this.usingAuth = false;
}
isGenerateModelsForLazyLoadAndCustomSelectionSet() {
return super.isGenerateModelsForLazyLoadAndCustomSelectionSet() && !this.config.isDataStoreEnabled;
}
generate() {
const shouldUseModelNameFieldInHasManyAndBelongsTo = true;
const shouldImputeKeyForUniDirectionalHasMany = false;
this.processDirectives(shouldUseModelNameFieldInHasManyAndBelongsTo, shouldImputeKeyForUniDirectionalHasMany);
if (this._parsedConfig.generate === appsync_visitor_1.CodeGenGenerateEnum.loader) {
return this.generateClassLoader();
}
else if (this._parsedConfig.generate === appsync_visitor_1.CodeGenGenerateEnum.metadata) {
return this.generateModelPathClasses();
}
(0, validate_field_name_1.validateFieldName)({ ...this.getSelectedModels(), ...this.getSelectedNonModels() });
if (this.selectedTypeIsEnum()) {
return this.generateEnums();
}
else if (this.selectedTypeIsNonModel()) {
return this.generateNonModelClasses();
}
return this.generateModelClasses();
}
generateClassLoader() {
const AMPLIFY_MODEL_VERSION = 'AMPLIFY_MODEL_VERSION';
const result = [this.generatePackageName(), '', this.generateImportStatements(java_config_1.LOADER_IMPORT_PACKAGES)];
result.push((0, visitor_plugin_common_1.transformComment)((0, ts_dedent_1.default) ` Contains the set of model classes that implement { Model}
interface.`));
const loaderClassDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.withName(java_config_1.LOADER_CLASS_NAME)
.access('public')
.final()
.asKind('class')
.implements(['ModelProvider']);
loaderClassDeclaration.addClassMember(AMPLIFY_MODEL_VERSION, 'String', `"${this.computeVersion()}"`, [], 'private', {
final: true,
static: true,
});
loaderClassDeclaration.addClassMember('amplifyGeneratedModelInstance', java_config_1.LOADER_CLASS_NAME, '', [], 'private', { static: true });
loaderClassDeclaration.addClassMethod(java_config_1.LOADER_CLASS_NAME, null, '', [], [], 'private');
const getInstanceBody = (0, ts_dedent_1.default) `
if (amplifyGeneratedModelInstance == null) {
amplifyGeneratedModelInstance = new ${java_config_1.LOADER_CLASS_NAME}();
}
return amplifyGeneratedModelInstance;`;
loaderClassDeclaration.addClassMethod('getInstance', java_config_1.LOADER_CLASS_NAME, getInstanceBody, [], [], 'public', {
static: true,
synchronized: true,
});
const modelsMethodDocString = (0, ts_dedent_1.default) `
Get a set of the model classes.
a set of the model classes.`;
const classList = Object.values(this.modelMap)
.map(model => `${this.getModelName(model)}.class`)
.join(', ');
const modelsMethodImplementation = `final Set<Class<? extends Model>> modifiableSet = new HashSet<>(
Arrays.<Class<? extends Model>>asList(${classList})
);
return Immutable.of(modifiableSet);
`;
loaderClassDeclaration.addClassMethod('models', 'Set<Class<? extends Model>>', modelsMethodImplementation, [], [], 'public', {}, ['Override'], undefined, modelsMethodDocString);
const versionMethodDocString = (0, ts_dedent_1.default) `
Get the version of the models.
the version string of the models.
`;
loaderClassDeclaration.addClassMethod('version', 'String', `return ${AMPLIFY_MODEL_VERSION};`, [], [], 'public', {}, ['Override'], undefined, versionMethodDocString);
result.push(loaderClassDeclaration.string);
return result.join('\n');
}
generateEnums() {
const result = [this.generatePackageName()];
Object.entries(this.getSelectedEnums()).forEach(([name, enumValue]) => {
const enumDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('enum')
.access('public')
.withName((0, change_case_1.pascalCase)(this.getEnumName(enumValue)))
.annotate(['SuppressWarnings("all")'])
.withComment('Auto generated enum from GraphQL schema.');
const body = Object.values(enumValue.values);
enumDeclaration.withBlock((0, visitor_plugin_common_1.indentMultiline)(body.join(',\n')));
result.push(enumDeclaration.string);
});
return result.join('\n');
}
generateModelClasses() {
const result = [];
Object.entries(this.getSelectedModels()).forEach(([name, model]) => {
const modelDeclaration = this.generateModelClass(model);
result.push(...[modelDeclaration]);
});
const packageDeclaration = this.generatePackageHeader();
return [packageDeclaration, ...result].join('\n');
}
generateNonModelClasses() {
const result = [];
Object.entries(this.getSelectedNonModels()).forEach(([name, type]) => {
const nonModelDeclaration = this.generateNonModelClass(type);
result.push(...[nonModelDeclaration]);
});
const packageDeclaration = this.generatePackageHeader(false);
return [packageDeclaration, ...result].join('\n');
}
generatePackageName() {
return `package ${java_config_1.GENERATED_PACKAGE_NAME};`;
}
generateModelClass(model) {
const classDeclarationBlock = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('class')
.access('public')
.withName(this.getModelName(model))
.implements(['Model'])
.withComment(`This is an auto generated class representing the ${model.name} type in your schema.`)
.final();
const annotations = this.generateModelAnnotations(model);
classDeclarationBlock.annotate(annotations);
if (this.isGenerateModelsForLazyLoadAndCustomSelectionSet()) {
this.generateRootPath(model, classDeclarationBlock);
}
const queryFields = this.getWritableFields(model);
queryFields.forEach(field => this.generateQueryFields(model, field, classDeclarationBlock));
const nonConnectedFields = this.getNonConnectedField(model);
model.fields.forEach(field => {
const value = nonConnectedFields.includes(field) ? '' : 'null';
this.generateModelField(field, value, classDeclarationBlock);
});
let isCompositeKey = false;
let isIdAsModelPrimaryKey = true;
if (this.config.respectPrimaryKeyAttributesOnConnectionField) {
const primaryKeyField = this.getModelPrimaryKeyField(model);
const { primaryKeyType, sortKeyFields } = primaryKeyField.primaryKeyInfo;
isCompositeKey = primaryKeyType === appsync_visitor_1.CodeGenPrimaryKeyType.CustomId && sortKeyFields.length > 0;
isIdAsModelPrimaryKey = primaryKeyType !== appsync_visitor_1.CodeGenPrimaryKeyType.CustomId;
}
if (this.isCustomPKEnabled() && isCompositeKey) {
this.generateIdentifierClassField(model, classDeclarationBlock);
}
this.generateStepBuilderInterfaces(model, isIdAsModelPrimaryKey).forEach((builderInterface) => {
classDeclarationBlock.nestedClass(builderInterface);
});
this.generateBuilderClass(model, classDeclarationBlock, isIdAsModelPrimaryKey);
this.generateCopyOfBuilderClass(model, classDeclarationBlock, isIdAsModelPrimaryKey);
if (this.isCustomPKEnabled()) {
this.generateModelIdentifierClass(model, classDeclarationBlock);
this.generateResolveIdentifier(model, classDeclarationBlock, isCompositeKey);
}
this.generateGetters(model, classDeclarationBlock);
this.generateConstructor(model, classDeclarationBlock);
this.generateEqualsMethod(model, classDeclarationBlock);
this.generateHashCodeMethod(model, classDeclarationBlock);
this.generateToStringMethod(model, classDeclarationBlock);
this.generateBuilderMethod(model, classDeclarationBlock, isIdAsModelPrimaryKey);
if (isIdAsModelPrimaryKey) {
this.generateJustIdMethod(model, classDeclarationBlock);
}
this.generateCopyOfBuilderMethod(model, classDeclarationBlock);
return classDeclarationBlock.string;
}
generateNonModelClass(nonModel) {
const classDeclarationBlock = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('class')
.access('public')
.withName(this.getModelName(nonModel))
.withComment(`This is an auto generated class representing the ${nonModel.name} type in your schema.`)
.final();
const nonConnectedFields = this.getNonConnectedField(nonModel);
nonModel.fields.forEach(field => {
const value = nonConnectedFields.includes(field) ? '' : 'null';
this.generateNonModelField(field, value, classDeclarationBlock);
});
this.generateStepBuilderInterfaces(nonModel, false).forEach((builderInterface) => {
classDeclarationBlock.nestedClass(builderInterface);
});
this.generateBuilderClass(nonModel, classDeclarationBlock, false);
this.generateCopyOfBuilderClass(nonModel, classDeclarationBlock, false);
this.generateGetters(nonModel, classDeclarationBlock);
this.generateConstructor(nonModel, classDeclarationBlock);
this.generateEqualsMethod(nonModel, classDeclarationBlock);
this.generateHashCodeMethod(nonModel, classDeclarationBlock);
this.generateBuilderMethod(nonModel, classDeclarationBlock, false);
this.generateCopyOfBuilderMethod(nonModel, classDeclarationBlock);
return classDeclarationBlock.string;
}
generatePackageHeader(isModel = true) {
let baseImports;
if (isModel) {
if (this.usingAuth) {
baseImports = java_config_1.MODEL_AUTH_CLASS_IMPORT_PACKAGES;
}
else {
baseImports = java_config_1.MODEL_CLASS_IMPORT_PACKAGES;
}
}
else {
baseImports = java_config_1.NON_MODEL_CLASS_IMPORT_PACKAGES;
}
const imports = this.generateImportStatements([...Array.from(this.additionalPackages), '', ...baseImports]);
return [this.generatePackageName(), '', imports].join('\n');
}
generateImportStatements(packages) {
return packages.map(pkg => (pkg ? `import ${pkg};` : '')).join('\n');
}
generateQueryFields(model, field, classDeclarationBlock) {
const queryFieldName = (0, change_case_1.constantCase)(field.name);
const fieldName = field.connectionInfo && field.connectionInfo.kind === process_connections_1.CodeGenConnectionType.BELONGS_TO
? field.connectionInfo.targetName
: this.getFieldName(field);
classDeclarationBlock.addClassMember(queryFieldName, 'QueryField', `field("${this.getModelName(model)}", "${fieldName}")`, [], 'public', {
final: true,
static: true,
});
}
generateModelField(field, value, classDeclarationBlock) {
const annotations = this.generateFieldAnnotations(field);
const fieldType = this.getNativeType(field);
const fieldName = this.getFieldName(field);
classDeclarationBlock.addClassMember(fieldName, fieldType, value, annotations, 'private', {
final: !field.isReadOnly,
});
}
generateNonModelField(field, value, classDeclarationBlock) {
const fieldType = this.getNativeType(field);
const fieldName = this.getFieldName(field);
classDeclarationBlock.addClassMember(fieldName, fieldType, value, [], 'private', {
final: true,
});
}
generateIdentifierClassField(model, classDeclaration) {
classDeclaration.addClassMember(this.getModelIdentifierClassFieldName(model), this.getModelIdentifierClassName(model), '', undefined, 'private');
}
generateStepBuilderInterfaces(model, isIdAsModelPrimaryKey = true) {
const nonNullableFields = this.getWritableFields(model).filter(field => this.isRequiredField(field));
const nullableFields = this.getWritableFields(model).filter(field => !this.isRequiredField(field));
const requiredInterfaces = nonNullableFields.filter((field) => !(isIdAsModelPrimaryKey && this.READ_ONLY_FIELDS.includes(field.name)));
const types = this.getTypesUsedByModel(model);
const interfaces = requiredInterfaces.map((field, idx) => {
const isLastField = requiredInterfaces.length - 1 === idx ? true : false;
const returnType = isLastField ? 'Build' : requiredInterfaces[idx + 1].name;
const interfaceName = this.getStepInterfaceName(field.name, types);
const methodName = this.getStepFunctionName(field);
const argumentType = this.getNativeType(field, true);
const argumentName = this.getStepFunctionArgumentName(field);
const interfaceDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('interface')
.withName(interfaceName)
.access('public');
interfaceDeclaration.withBlock((0, visitor_plugin_common_1.indent)(`${this.getStepInterfaceName(returnType, types)} ${methodName}(${argumentType} ${argumentName});`));
return interfaceDeclaration;
});
const builder = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('interface')
.withName(this.getStepInterfaceName('Build', types))
.access('public');
const builderBody = [];
builderBody.push(`${this.getModelName(model)} build();`);
if (isIdAsModelPrimaryKey) {
builderBody.push(`${this.getStepInterfaceName('Build', types)} id(String id);`);
}
nullableFields.forEach(field => {
const fieldName = this.getStepFunctionArgumentName(field);
const methodName = this.getStepFunctionName(field);
builderBody.push(`${this.getStepInterfaceName('Build', types)} ${methodName}(${this.getNativeType(field, true)} ${fieldName});`);
});
builder.withBlock((0, visitor_plugin_common_1.indentMultiline)(builderBody.join('\n')));
return [...interfaces, builder];
}
generateBuilderClass(model, classDeclaration, isIdAsModelPrimaryKey = true) {
const nonNullableFields = this.getWritableFields(model).filter(field => this.isRequiredField(field));
const nullableFields = this.getWritableFields(model).filter(field => !this.isRequiredField(field));
const stepFields = nonNullableFields.filter((field) => !(isIdAsModelPrimaryKey && this.READ_ONLY_FIELDS.includes(field.name)));
const types = this.getTypesUsedByModel(model);
const stepInterfaces = stepFields.map((field) => this.getStepInterfaceName(field.name, types));
const builderClassDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.access('public')
.static()
.asKind('class')
.withName('Builder')
.implements([...stepInterfaces, this.getStepInterfaceName('Build', types)]);
[...nonNullableFields, ...nullableFields].forEach((field) => {
const fieldName = this.getFieldName(field);
builderClassDeclaration.addClassMember(fieldName, this.getNativeType(field), '', undefined, 'private');
});
builderClassDeclaration.addClassMethod("Builder", null, "", [], [], 'public');
const constructorArguments = this.getWritableFields(model).map(field => ({
name: this.getFieldName(field),
type: this.getNativeType(field),
}));
const constructorBody = (0, ts_dedent_1.default)(this.getWritableFields(model)
.map(field => {
const fieldName = this.getFieldName(field);
return `this.${fieldName} = ${fieldName};`;
})
.join('\n')).trim();
builderClassDeclaration.addClassMethod("Builder", null, constructorBody, constructorArguments, [], 'private');
const buildImplementation = isIdAsModelPrimaryKey
? [`String id = this.id != null ? this.id : UUID.randomUUID().toString();`, '']
: [''];
const buildParams = this.getWritableFields(model)
.map(field => this.getFieldName(field))
.join(',\n');
buildImplementation.push(`return new ${this.getModelName(model)}(\n${(0, visitor_plugin_common_1.indentMultiline)(buildParams)});`);
builderClassDeclaration.addClassMethod('build', this.getModelName(model), (0, visitor_plugin_common_1.indentMultiline)(buildImplementation.join('\n')), undefined, [], 'public', {}, ['Override']);
stepFields.forEach((field, idx, fields) => {
const isLastStep = idx === fields.length - 1;
const fieldName = this.getFieldName(field);
const methodName = this.getStepFunctionName(field);
const returnType = isLastStep ? this.getStepInterfaceName('Build', types) : this.getStepInterfaceName(fields[idx + 1].name, types);
const argumentType = this.getNativeType(field, true);
const argumentName = this.getStepFunctionArgumentName(field);
const assignment = this.isModelReference(field) ?
`this.${fieldName} = new LoadedModelReferenceImpl<>(${argumentName});` :
`this.${fieldName} = ${argumentName};`;
const body = [`Objects.requireNonNull(${argumentName});`, `${assignment}`, `return this;`].join('\n');
builderClassDeclaration.addClassMethod(methodName, returnType, (0, visitor_plugin_common_1.indentMultiline)(body), [{ name: argumentName, type: argumentType }], [], 'public', {}, ['Override']);
});
nullableFields.forEach((field) => {
const fieldName = this.getFieldName(field);
const methodName = this.getStepFunctionName(field);
const returnType = this.getStepInterfaceName('Build', types);
const argumentType = this.getNativeType(field, true);
const argumentName = this.getStepFunctionArgumentName(field);
const assignment = this.isModelReference(field) ?
`this.${fieldName} = new LoadedModelReferenceImpl<>(${argumentName});` :
`this.${fieldName} = ${argumentName};`;
const body = [`${assignment}`, `return this;`].join('\n');
builderClassDeclaration.addClassMethod(methodName, returnType, (0, visitor_plugin_common_1.indentMultiline)(body), [{ name: argumentName, type: argumentType }], [], 'public', {}, ['Override']);
});
if (isIdAsModelPrimaryKey) {
const idBuildStepBody = (0, ts_dedent_1.default) `this.id = id;
return this;`;
const idComment = (0, ts_dedent_1.default) `
id id
Current Builder instance, for fluent method chaining`;
builderClassDeclaration.addClassMethod('id', this.getStepInterfaceName('Build', types), (0, visitor_plugin_common_1.indentMultiline)(idBuildStepBody), [{ name: 'id', type: 'String' }], [], 'public', {}, [], [], idComment);
}
classDeclaration.nestedClass(builderClassDeclaration);
}
generateCopyOfBuilderClass(model, classDeclaration, isIdAsModelPrimaryKey = true) {
const builderName = 'CopyOfBuilder';
const copyOfBuilderClassDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.access('public')
.final()
.asKind('class')
.withName(builderName)
.extends(['Builder']);
const nonNullableFields = this.getWritableFields(model)
.filter(field => this.isRequiredField(field))
.filter(f => (isIdAsModelPrimaryKey ? f.name !== 'id' : true));
const nullableFields = this.getWritableFields(model).filter(field => !this.isRequiredField(field));
const constructorArguments = this.getWritableFields(model).map(field => ({
name: this.getStepFunctionArgumentName(field),
type: this.getNativeType(field),
}));
const constructorNullChecks = (0, ts_dedent_1.default)(nonNullableFields.map(field => `Objects.requireNonNull(${this.getFieldName(field)});`).join('\n')).trim();
const superArgs = (0, ts_dedent_1.default)(this.getWritableFields(model)
.map(field => this.getFieldName(field))
.join(', ')).trim();
copyOfBuilderClassDeclaration.addClassMethod(builderName, null, `super(${superArgs});\n${constructorNullChecks}`, constructorArguments, [], 'private');
[...nonNullableFields, ...nullableFields].forEach(field => {
const methodName = this.getStepFunctionName(field);
const argumentName = this.getStepFunctionArgumentName(field);
const argumentType = this.getNativeType(field, true);
const implementation = `return (${builderName}) super.${methodName}(${argumentName});`;
copyOfBuilderClassDeclaration.addClassMethod(methodName, builderName, implementation, [
{
name: argumentName,
type: argumentType,
},
], [], 'public', {}, ['Override']);
});
classDeclaration.nestedClass(copyOfBuilderClassDeclaration);
}
generateModelIdentifierClass(model, classDeclaration) {
const primaryKeyField = this.getModelPrimaryKeyField(model);
const { sortKeyFields } = primaryKeyField.primaryKeyInfo;
this.additionalPackages.add(java_config_1.CUSTOM_PRIMARY_KEY_IMPORT_PACKAGE);
const modelPrimaryKeyClassName = this.getModelIdentifierClassName(model);
const primaryKeyClassDeclaration = new java_declaration_block_1.JavaDeclarationBlock()
.access('public')
.static()
.asKind('class')
.withName(modelPrimaryKeyClassName)
.extends([`ModelIdentifier<${this.getModelName(model)}>`]);
primaryKeyClassDeclaration.addClassMember('serialVersionUID', 'long', '1L', [], 'private', { static: true, final: true });
const primaryKeyComponentFields = [primaryKeyField, ...sortKeyFields];
const constructorParams = primaryKeyComponentFields.map(field => ({ name: this.getFieldName(field), type: this.getNativeType(field) }));
const constructorImpl = `super(${primaryKeyComponentFields.map(field => this.getFieldName(field)).join(', ')});`;
primaryKeyClassDeclaration.addClassMethod(modelPrimaryKeyClassName, null, constructorImpl, constructorParams, [], 'public');
classDeclaration.nestedClass(primaryKeyClassDeclaration);
}
generateCopyOfBuilderMethod(model, classDeclaration) {
const args = (0, visitor_plugin_common_1.indentMultiline)(this.getWritableFields(model)
.map(field => this.getFieldName(field))
.join(',\n')).trim();
const methodBody = `return new CopyOfBuilder(${args});`;
classDeclaration.addClassMethod('copyOfBuilder', 'CopyOfBuilder', methodBody, [], [], 'public');
}
generateResolveIdentifier(model, declarationsBlock, isCompositeKey) {
const primaryKeyField = this.getModelPrimaryKeyField(model);
const { sortKeyFields } = primaryKeyField.primaryKeyInfo;
const modelIdentifierClassFieldName = this.getModelIdentifierClassFieldName(model);
const returnType = isCompositeKey ? this.getModelIdentifierClassName(model) : this.getNativeType(primaryKeyField);
const body = isCompositeKey
? [
`if (${modelIdentifierClassFieldName} == null) {`,
(0, visitor_plugin_common_1.indent)(`this.${modelIdentifierClassFieldName} = new ${this.getModelIdentifierClassName(model)}(${[primaryKeyField, ...sortKeyFields]
.map(f => this.getFieldName(f))
.join(', ')});`),
'}',
`return ${modelIdentifierClassFieldName};`,
].join('\n')
: `return ${this.getFieldName(primaryKeyField)};`;
declarationsBlock.addClassMethod('resolveIdentifier', returnType, body, [], [], 'public', {}, ['Deprecated'], [], '@deprecated This API is internal to Amplify and should not be used.');
}
getModelIdentifierClassName(model) {
return `${this.getModelName(model)}Identifier`;
}
getModelIdentifierClassFieldName(model) {
const className = this.getModelIdentifierClassName(model);
return className.charAt(0).toLowerCase() + className.slice(1);
}
generateGetters(model, declarationsBlock) {
model.fields.forEach((field) => {
const fieldName = this.getFieldName(field);
const returnType = this.getNativeType(field);
const methodName = this.getFieldGetterName(field);
const body = (0, visitor_plugin_common_1.indent)(`return ${fieldName};`);
declarationsBlock.addClassMethod(methodName, returnType, body, undefined, undefined, 'public');
});
}
getFieldGetterName(field) {
return `get${(0, change_case_1.pascalCase)(field.name)}`;
}
getStepFunctionName(field) {
return (0, change_case_1.camelCase)(field.name);
}
getStepFunctionArgumentName(field) {
return (0, change_case_1.camelCase)(field.name);
}
generateConstructor(model, declarationsBlock) {
const name = this.getModelName(model);
const body = this.getWritableFields(model)
.map((field) => {
const fieldName = this.getFieldName(field);
return `this.${fieldName} = ${fieldName};`;
})
.join('\n');
const constructorArguments = this.getWritableFields(model).map(field => ({
name: this.getFieldName(field),
type: this.getNativeType(field),
}));
declarationsBlock.addClassMethod(name, null, body, constructorArguments, undefined, 'private');
}
getNativeType(field, unwrapModelReference = false) {
const nativeType = super.getNativeType(field);
if (Object.keys(scalars_1.JAVA_TYPE_IMPORT_MAP).includes(nativeType)) {
this.additionalPackages.add(scalars_1.JAVA_TYPE_IMPORT_MAP[nativeType]);
}
if (!unwrapModelReference && this.isModelReference(field)) {
return `ModelReference<${nativeType}>`;
}
return nativeType;
}
getListType(typeStr, field) {
if (this.isModelList(field)) {
return `ModelList<${typeStr}>`;
}
else {
return super.getListType(typeStr, field);
}
}
isModelReference(field) {
var _a;
if (!this.isGenerateModelsForLazyLoadAndCustomSelectionSet())
return false;
switch ((_a = field.connectionInfo) === null || _a === void 0 ? void 0 : _a.kind) {
case process_connections_1.CodeGenConnectionType.BELONGS_TO:
case process_connections_1.CodeGenConnectionType.HAS_ONE:
return true;
default:
return false;
}
}
isModelList(field) {
var _a;
return this.isGenerateModelsForLazyLoadAndCustomSelectionSet() && ((_a = field.connectionInfo) === null || _a === void 0 ? void 0 : _a.kind) == process_connections_1.CodeGenConnectionType.HAS_MANY;
}
generateEqualsMethod(model, declarationBlock) {
const paramName = 'obj';
const className = this.getModelName(model);
const instanceName = (0, change_case_1.camelCase)(model.name);
const body = [
`if (this == ${paramName}) {`,
' return true;',
`} else if(${paramName} == null || getClass() != ${paramName}.getClass()) {`,
' return false;',
'} else {',
];
body.push(`${className} ${instanceName} = (${className}) ${paramName};`);
const propCheck = (0, visitor_plugin_common_1.indentMultiline)(this.getNonConnectedField(model)
.map(field => {
const getterName = this.getFieldGetterName(field);
return `ObjectsCompat.equals(${getterName}(), ${instanceName}.${getterName}())`;
})
.join(' &&\n'), 4).trim();
body.push(`return ${propCheck};`);
body.push('}');
declarationBlock.addClassMethod('equals', 'boolean', (0, visitor_plugin_common_1.indentMultiline)(body.join('\n')), [{ name: paramName, type: 'Object' }], [], 'public', {}, ['Override']);
}
generateHashCodeMethod(model, declarationBlock) {
const body = [
'return new StringBuilder()',
...this.getNonConnectedField(model).map(field => `.append(${this.getFieldGetterName(field)}())`),
'.toString()',
'.hashCode();',
].join('\n');
declarationBlock.addClassMethod('hashCode', 'int', (0, visitor_plugin_common_1.indentMultiline)(body).trimLeft(), [], [], 'public', {}, ['Override']);
}
generateToStringMethod(model, declarationBlock) {
const fields = this.getNonConnectedField(model).map(field => {
const fieldName = this.getFieldName(field);
const fieldGetterName = this.getFieldGetterName(field);
return '"' + fieldName + '=" + String.valueOf(' + fieldGetterName + '())';
});
const body = [
'return new StringBuilder()',
`.append("${this.getModelName(model)} {")`,
fields
.map((field, index) => {
let fieldDelimiter = '';
if (fields.length - index - 1 !== 0) {
fieldDelimiter = ' + ", "';
}
return '.append(' + field + fieldDelimiter + ')';
})
.join('\n'),
'.append("}")',
'.toString();',
];
declarationBlock.addClassMethod('toString', 'String', (0, visitor_plugin_common_1.indentMultiline)(body.join('\n')).trimLeft(), [], [], 'public', {}, ['Override']);
}
generateBuilderMethod(model, classDeclaration, isIdAsModelPrimaryKey = true) {
const requiredFields = this.getWritableFields(model).filter(field => this.isRequiredField(field) && !(isIdAsModelPrimaryKey && this.READ_ONLY_FIELDS.includes(field.name)));
const types = this.getTypesUsedByModel(model);
const returnType = requiredFields.length
? this.getStepInterfaceName(requiredFields[0].name, types)
: this.getStepInterfaceName('Build', types);
classDeclaration.addClassMethod('builder', returnType, (0, visitor_plugin_common_1.indentMultiline)(`return new Builder();`), [], [], 'public', { static: true }, []);
}
getStepInterfaceName(nextFieldName, types) {
const pascalCaseFieldName = (0, change_case_1.pascalCase)(nextFieldName);
const stepInterfaceName = `${pascalCaseFieldName}Step`;
if (types.has(stepInterfaceName)) {
return `${pascalCaseFieldName}BuildStep`;
}
return stepInterfaceName;
}
getTypesUsedByModel(model) {
return new Set(model.fields.map(field => field.type));
}
generateModelAnnotations(model) {
const annotations = model.directives.map(directive => {
switch (directive.name) {
case 'model':
const modelArgs = [];
const authDirectives = model.directives.filter(d => d.name === 'auth');
const authRules = this.generateAuthRules(authDirectives);
modelArgs.push(`pluralName = "${this.pluralizeModelName(model)}"`);
if (this.isCustomPKEnabled()) {
modelArgs.push(`type = Model.Type.USER`);
modelArgs.push(`version = 1`);
}
if (authRules.length) {
this.usingAuth = true;
modelArgs.push(`authRules = ${authRules}`);
}
if (this.isGenerateModelsForLazyLoadAndCustomSelectionSet()) {
modelArgs.push(`hasLazySupport = true`);
}
return `ModelConfig(${modelArgs.join(', ')})`;
case 'key':
const keyArgs = [];
keyArgs.push(`name = "${directive.arguments.name}"`);
keyArgs.push(`fields = {${directive.arguments.fields.map((f) => `"${f}"`).join(',')}}`);
return `Index(${keyArgs.join(', ')})`;
default:
break;
}
return '';
});
return ['SuppressWarnings("all")', ...annotations].filter(annotation => annotation);
}
generateAuthRules(authDirectives) {
const operationMapping = {
create: 'ModelOperation.CREATE',
read: 'ModelOperation.READ',
update: 'ModelOperation.UPDATE',
delete: 'ModelOperation.DELETE',
};
const rules = [];
authDirectives.forEach(directive => {
var _a;
(_a = directive.arguments) === null || _a === void 0 ? void 0 : _a.rules.forEach(rule => {
var _a, _b;
const authRule = [];
switch (rule.allow) {
case process_auth_1.AuthStrategy.owner:
authRule.push('allow = AuthStrategy.OWNER');
authRule.push(`ownerField = "${rule.ownerField}"`);
authRule.push(`identityClaim = "${rule.identityClaim}"`);
break;
case process_auth_1.AuthStrategy.private:
authRule.push('allow = AuthStrategy.PRIVATE');
break;
case process_auth_1.AuthStrategy.public:
authRule.push('allow = AuthStrategy.PUBLIC');
break;
case process_auth_1.AuthStrategy.groups:
authRule.push('allow = AuthStrategy.GROUPS');
authRule.push(`groupClaim = "${rule.groupClaim}"`);
if (rule.groups) {
authRule.push(`groups = { ${(_a = rule.groups) === null || _a === void 0 ? void 0 : _a.map(group => `"${group}"`).join(', ')} }`);
}
else {
authRule.push(`groupsField = "${rule.groupField}"`);
}
break;
default:
(0, warn_1.printWarning)(`Model has auth with authStrategy ${rule.allow} of which is not yet supported`);
return;
}
if (rule.provider != null) {
authRule.push(`provider = "${rule.provider}"`);
}
authRule.push(`operations = { ${(_b = rule.operations) === null || _b === void 0 ? void 0 : _b.map(op => operationMapping[op]).join(', ')} }`);
rules.push(``);
});
});
if (rules.length) {
return ['{', `${(0, visitor_plugin_common_1.indentMultiline)(rules.join(',\n'))}`, '}'].join('\n');
}
return '';
}
generateFieldAnnotations(field) {
const annotations = [];
annotations.push(this.generateModelFieldAnnotation(field));
annotations.push(this.generateConnectionAnnotation(field));
return annotations.filter(annotation => annotation);
}
generateModelFieldAnnotation(field) {
const authDirectives = field.directives.filter(d => d.name === 'auth');
const authRules = this.generateAuthRules(authDirectives);
if (authRules.length) {
this.usingAuth = true;
}
const annotationArgs = [
`targetType="${field.type}"`,
this.isRequiredField(field) ? 'isRequired = true' : '',
authRules.length ? `authRules = ${authRules}` : '',
field.isReadOnly ? 'isReadOnly = true' : '',
].filter(arg => arg);
return `ModelField${annotationArgs.length ? `(${annotationArgs.join(', ')})` : ''}`;
}
generateConnectionAnnotation(field) {
if (!field.connectionInfo)
return '';
const { connectionInfo } = field;
this.additionalPackages.add(java_config_1.CONNECTION_RELATIONSHIP_IMPORTS[connectionInfo.kind]);
if (this.isGenerateModelsForLazyLoadAndCustomSelectionSet()) {
java_config_1.CONNECTION_RELATIONSHIP_LAZY_LOAD_IMPORTS[connectionInfo.kind].forEach(item => this.additionalPackages.add(item));
}
let connectionDirectiveName = '';
const connectionArguments = [];
switch (connectionInfo.kind) {
case process_connections_1.CodeGenConnectionType.HAS_ONE:
connectionDirectiveName = 'HasOne';
if (connectionInfo.associatedWithNativeReferences) {
connectionArguments.push(`associatedWith = "${this.getFieldName(connectionInfo.associatedWithNativeReferences)}"`);
}
else {
connectionArguments.push(`associatedWith = "${this.getFieldName(connectionInfo.associatedWith)}"`);
}
if (this.isCustomPKEnabled() && this.isGenerateModelsForLazyLoadAndCustomSelectionSet() && connectionInfo.targetNames) {
const hasOneTargetNamesArgs = `targetNames = {${connectionInfo.targetNames.map(target => `"${target}"`).join(', ')}}`;
connectionArguments.push(hasOneTargetNamesArgs);
}
break;
case process_connections_1.CodeGenConnectionType.HAS_MANY:
connectionDirectiveName = 'HasMany';
if (connectionInfo.associatedWithNativeReferences) {
connectionArguments.push(`associatedWith = "${this.getFieldName(connectionInfo.associatedWithNativeReferences)}"`);
}
else {
connectionArguments.push(`associatedWith = "${this.getFieldName(connectionInfo.associatedWith)}"`);
}
break;
case process_connections_1.CodeGenConnectionType.BELONGS_TO:
connectionDirectiveName = 'BelongsTo';
const belongsToTargetNameArgs = `targetName = "${connectionInfo.targetName}"`;
connectionArguments.push(belongsToTargetNameArgs);
if (this.isCustomPKEnabled()) {
const belongsToTargetNamesArgs = `targetNames = {${connectionInfo.targetNames.map(target => `"${target}"`).join(', ')}}`;
connectionArguments.push(belongsToTargetNamesArgs);
}
break;
}
connectionArguments.push(`type = ${this.getModelName(connectionInfo.connectedModel)}.class`);
return `${connectionDirectiveName}${connectionArguments.length ? `(${connectionArguments.join(', ')})` : ''}`;
}
generateJustIdMethod(model, classDeclaration) {
const returnType = this.getModelName(model);
const comment = (0, ts_dedent_1.default) `WARNING: This method should not be used to build an instance of this object for a CREATE mutation.
This is a convenience method to return an instance of the object with only its ID populated
to be used in the context of a parameter in a delete mutation or referencing a foreign key
in a relationship.
id the id of the existing item this instance will represent
an instance of this model with only ID populated`;
const initArgs = (0, visitor_plugin_common_1.indentMultiline)(['id', ...new Array(this.getWritableFields(model).length - 1).fill('null')].join(',\n'));
const initBlock = `return new ${returnType}(\n${initArgs}\n);`;
classDeclaration.addClassMethod('justId', returnType, initBlock, [{ name: 'id', type: 'String' }], [], 'public', { static: true }, [], [], comment);
}
getNonConnectedField(model) {
return model.fields.filter(f => {
if (!f.connectionInfo)
return true;
if (f.connectionInfo.kind == process_connections_1.CodeGenConnectionType.BELONGS_TO) {
return true;
}
});
}
getWritableFields(model) {
return this.getNonConnectedField(model).filter(f => !f.isReadOnly);
}
getConnectedFields(model) {
return model.fields.filter(f => {
if (f.connectionInfo)
return true;
});
}
generateRootPath(model, classDeclarationBlock) {
const modelPathName = this.generateModelPathName(model);
classDeclarationBlock.addClassMember("rootPath", modelPathName, `new ${modelPathName}("root", false, null)`, [], 'public', {
final: true,
static: true,
});
}
generateModelPathClasses() {
const result = [];
Object.entries(this.getSelectedModels()).forEach(([name, model]) => {
const modelPathDeclaration = this.generateModelPathClass(model);
result.push(...[modelPathDeclaration]);
});
const packageDeclaration = this.generateModelPathPackageHeader();
return [packageDeclaration, ...result].join('\n');
}
generateModelPathClass(model) {
const classDeclarationBlock = new java_declaration_block_1.JavaDeclarationBlock()
.asKind('class')
.access('public')
.withName(this.generateModelPathName(model))
.extends([`ModelPath<${this.getModelName(model)}>`])
.withComment(`This is an auto generated class representing the ModelPath for the ${model.name} type in your schema.`)
.final();
this.generateModelPathConstructor(model, classDeclarationBlock);
const connectedFields = this.getConnectedFields(model);
connectedFields.forEach(field => this.generateModelPathField(field, classDeclarationBlock));
return classDeclarationBlock.string;
}
generateModelPathName(model) {
return this.getModelName(model) + "Path";
}
generateModelPathField(field, classDeclarationBlock) {
const fieldName = this.getFieldName(field);
const fieldType = this.generateModelPathName(this.modelMap[field.type]);
classDeclarationBlock.addClassMember(fieldName, fieldType, '', [], 'private');
const methodName = this.getFieldGetterName(field);
const modelPathFieldGetterBody = (0, ts_dedent_1.default) `
if (${fieldName} == null) {
${fieldName} = new ${fieldType}("${fieldName}", ${field.isList}, this);
}
return ${fieldName};`;
classDeclarationBlock.addClassMethod(methodName, fieldType, modelPathFieldGetterBody, undefined, undefined, 'public', {
synchronized: true,
});
}
;
generateModelPathPackageHeader() {
const imports = this.generateImportStatements(java_config_1.MODEL_PATH_CLASS_IMPORT_PACKAGES);
return [this.generatePackageName(), '', imports].join('\n');
}
generateModelPathConstructor(model, declarationsBlock) {
const modelPathName = this.generateModelPathName(model);
const constructorArguments = [
{ name: "name", type: "@NonNull String" },
{ name: "isCollection", type: "@NonNull Boolean" },
{ name: "parent", type: "@Nullable PropertyPath" }
];
const body = `super(name, isCollection, parent, ${this.getModelName(model)}.class);`;
declarationsBlock.addClassMethod(modelPathName, null, body, constructorArguments, undefined, '');
}
}
exports.AppSyncModelJavaVisitor = AppSyncModelJavaVisitor;
//# sourceMappingURL=appsync-java-visitor.js.map