aws-amplify / amplify-codegen

Amplify Codegen is a JavaScript toolkit library for frontend and mobile developers building Amplify applications.
Apache License 2.0
58 stars 59 forks source link

Generated client (flutter) code: ModelProvider has type error #862

Open sisygoboom opened 1 month ago

sisygoboom commented 1 month ago

Environment information

System:
  OS: Windows 10 10.0.19045
  CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
  Memory: 13.23 GB / 31.93 GB
Binaries:
  Node: 18.18.2 - C:\Program Files\nodejs\node.EXE
  Yarn: undefined - undefined
  npm: 9.8.1 - C:\Program Files\nodejs\npm.CMD
  pnpm: 8.10.5 - C:\Program Files\nodejs\pnpm.CMD
NPM Packages:
  @aws-amplify/backend: 1.0.4
  @aws-amplify/backend-cli: 1.2.0
  aws-amplify: 6.4.3
  aws-cdk: Not Found
  aws-cdk-lib: 2.150.0
  typescript: 5.5.4
AWS environment variables:
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
  AWS_STS_REGIONAL_ENDPOINTS = regional
No CDK environment variables

Description

Using npx ampx generate graphql-client-code --format modelgen --model-target dart --out ./lib/models as recommended in the documentation, generates code with a type error:

image

Generated code

// ChatTable.dart

/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
*  http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

// NOTE: This file is generated and may not follow lint rules defined in your app
// Generated files can be excluded from analysis in analysis_options.yaml
// For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis

// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, override_on_non_overriding_member, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously

import 'ModelProvider.dart';
import 'package:amplify_core/amplify_core.dart' as amplify_core;

/** This is an auto generated class representing the ChatTable type in your schema. */
class ChatTable {
  final String? _pk;
  final String? _sk;
  final String? _message;
  final String? _userId;
  final String? _timestamp;

  String get pk {
    try {
      return _pk!;
    } catch(e) {
      throw amplify_core.AmplifyCodeGenModelException(
          amplify_core.AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage,
          recoverySuggestion:
            amplify_core.AmplifyExceptionMessages.codeGenRequiredFieldForceCastRecoverySuggestion,
          underlyingException: e.toString()
          );
    }
  }

  String get sk {
    try {
      return _sk!;
    } catch(e) {
      throw amplify_core.AmplifyCodeGenModelException(
          amplify_core.AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage,
          recoverySuggestion:
            amplify_core.AmplifyExceptionMessages.codeGenRequiredFieldForceCastRecoverySuggestion,
          underlyingException: e.toString()
          );
    }
  }

  String? get message {
    return _message;
  }

  String? get userId {
    return _userId;
  }

  String? get timestamp {
    return _timestamp;
  }

  const ChatTable._internal({required pk, required sk, message, userId, timestamp}): _pk = pk, _sk = sk, _message = message, _userId = userId, _timestamp = timestamp;

  factory ChatTable({required String pk, required String sk, String? message, String? userId, String? timestamp}) {
    return ChatTable._internal(
      pk: pk,
      sk: sk,
      message: message,
      userId: userId,
      timestamp: timestamp);
  }

  bool equals(Object other) {
    return this == other;
  }

  @override
  bool operator ==(Object other) {
    if (identical(other, this)) return true;
    return other is ChatTable &&
      _pk == other._pk &&
      _sk == other._sk &&
      _message == other._message &&
      _userId == other._userId &&
      _timestamp == other._timestamp;
  }

  @override
  int get hashCode => toString().hashCode;

  @override
  String toString() {
    var buffer = new StringBuffer();

    buffer.write("ChatTable {");
    buffer.write("pk=" + "$_pk" + ", ");
    buffer.write("sk=" + "$_sk" + ", ");
    buffer.write("message=" + "$_message" + ", ");
    buffer.write("userId=" + "$_userId" + ", ");
    buffer.write("timestamp=" + "$_timestamp");
    buffer.write("}");

    return buffer.toString();
  }

  ChatTable copyWith({String? pk, String? sk, String? message, String? userId, String? timestamp}) {
    return ChatTable._internal(
      pk: pk ?? this.pk,
      sk: sk ?? this.sk,
      message: message ?? this.message,
      userId: userId ?? this.userId,
      timestamp: timestamp ?? this.timestamp);
  }

  ChatTable copyWithModelFieldValues({
    ModelFieldValue<String>? pk,
    ModelFieldValue<String>? sk,
    ModelFieldValue<String?>? message,
    ModelFieldValue<String?>? userId,
    ModelFieldValue<String?>? timestamp
  }) {
    return ChatTable._internal(
      pk: pk == null ? this.pk : pk.value,
      sk: sk == null ? this.sk : sk.value,
      message: message == null ? this.message : message.value,
      userId: userId == null ? this.userId : userId.value,
      timestamp: timestamp == null ? this.timestamp : timestamp.value
    );
  }

  ChatTable.fromJson(Map<String, dynamic> json)  
    : _pk = json['pk'],
      _sk = json['sk'],
      _message = json['message'],
      _userId = json['userId'],
      _timestamp = json['timestamp'];

  Map<String, dynamic> toJson() => {
    'pk': _pk, 'sk': _sk, 'message': _message, 'userId': _userId, 'timestamp': _timestamp
  };

  Map<String, Object?> toMap() => {
    'pk': _pk,
    'sk': _sk,
    'message': _message,
    'userId': _userId,
    'timestamp': _timestamp
  };

  static var schema = amplify_core.Model.defineSchema(define: (amplify_core.ModelSchemaDefinition modelSchemaDefinition) {
    modelSchemaDefinition.name = "ChatTable";
    modelSchemaDefinition.pluralName = "ChatTables";

    modelSchemaDefinition.addField(amplify_core.ModelFieldDefinition.customTypeField(
      fieldName: 'pk',
      isRequired: true,
      ofType: amplify_core.ModelFieldType(amplify_core.ModelFieldTypeEnum.string)
    ));

    modelSchemaDefinition.addField(amplify_core.ModelFieldDefinition.customTypeField(
      fieldName: 'sk',
      isRequired: true,
      ofType: amplify_core.ModelFieldType(amplify_core.ModelFieldTypeEnum.string)
    ));

    modelSchemaDefinition.addField(amplify_core.ModelFieldDefinition.customTypeField(
      fieldName: 'message',
      isRequired: false,
      ofType: amplify_core.ModelFieldType(amplify_core.ModelFieldTypeEnum.string)
    ));

    modelSchemaDefinition.addField(amplify_core.ModelFieldDefinition.customTypeField(
      fieldName: 'userId',
      isRequired: false,
      ofType: amplify_core.ModelFieldType(amplify_core.ModelFieldTypeEnum.string)
    ));

    modelSchemaDefinition.addField(amplify_core.ModelFieldDefinition.customTypeField(
      fieldName: 'timestamp',
      isRequired: false,
      ofType: amplify_core.ModelFieldType(amplify_core.ModelFieldTypeEnum.string)
    ));
  });
}
// ModelProvider.dart

/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
*  http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

// NOTE: This file is generated and may not follow lint rules defined in your app
// Generated files can be excluded from analysis in analysis_options.yaml
// For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis

// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, override_on_non_overriding_member, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously

import 'package:amplify_core/amplify_core.dart' as amplify_core;
import 'ChatTable.dart';

export 'ChatTable.dart';

class ModelProvider implements amplify_core.ModelProviderInterface {
  @override
  String version = "c087081b6e396e9ccf6e84954b9ab9de";
  @override
  List<amplify_core.ModelSchema> modelSchemas = [];
  @override
  List<amplify_core.ModelSchema> customTypeSchemas = [ChatTable.schema];
  static final ModelProvider _instance = ModelProvider();

  static ModelProvider get instance => _instance;
}

class ModelFieldValue<T> {
  const ModelFieldValue.value(this.value);

  final T value;
}

Related user generated code

// amplify/data/resource.ts

import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
import { getMessagesByConversationId } from '../functions/getMessagesByConversationId/resource';
import { sendMessage } from '../functions/sendMessage/resource';

const schema = a.schema({
  ChatTable: a.customType({
    pk: a.string().required(),
    sk: a.string().required(),
    message: a.string(),
    userId: a.string(),
    timestamp: a.string(),
  }),
  getMessagesByConversationId: a
    .query()
    .arguments({
      conversationId: a.string().required(),
      lastKey: a.string(),
    })
    .returns(a.ref('ChatTable').array())
    .handler(a.handler.function(getMessagesByConversationId))
    .authorization(allow => [allow.publicApiKey()]),
  sendMessage: a
    .mutation()
    .arguments({
      userId: a.string().required(),
      message: a.string().required(),
      conversationId: a.string().required(),
      //timestamp: a.string().required(),
    })
    .returns(a.ref('ChatTable'))
    .handler(a.handler.function(sendMessage))
    .authorization(allow => [allow.publicApiKey()]),
})
.authorization(allow => [
  allow.resource(getMessagesByConversationId).to(['query']),
  allow.resource(sendMessage).to(['mutate']),
]);

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'apiKey',
    apiKeyAuthorizationMode: {
      expiresInDays: 30
    },
  },
  functions: {
    getMessagesByConversationId,
    sendMessage,
  }
});
sundersc commented 1 month ago

Marking it as a bug. Codegen expects at least one model in the schema to add the getModelTypeByModelName block.

Workaround: Add a dummy model to the schema.

Dummy: a.model({
      description: a.string()
    })
    .authorization(allow => [allow.publicApiKey()])