UNPKG

@aws-amplify/appsync-modelgen-plugin

Version:
827 lines (824 loc) 45.3 kB
"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 {@link 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. @return 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. @return 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) ` @param id id @return 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(`@AuthRule(${authRule.join(', ')})`); }); }); 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. @param id the id of the existing item this instance will represent @return 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