aws-amplify / amplify-codegen

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

Generated dart client code has a type error #878

Open sisygoboom opened 2 months ago

sisygoboom commented 2 months ago

Environment information

System:
  OS: Windows 10 10.0.19045
  CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
  Memory: 11.76 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/auth-construct: 1.3.0
  @aws-amplify/backend: 1.2.0
  @aws-amplify/backend-auth: 1.1.3
  @aws-amplify/backend-cli: 1.2.5
  @aws-amplify/backend-data: 1.1.3
  @aws-amplify/backend-deployer: 1.1.0
  @aws-amplify/backend-function: 1.3.4
  @aws-amplify/backend-output-schemas: 1.2.0
  @aws-amplify/backend-output-storage: 1.1.1
  @aws-amplify/backend-secret: 1.1.0
  @aws-amplify/backend-storage: 1.1.2
  @aws-amplify/cli-core: 1.1.2
  @aws-amplify/client-config: 1.3.0
  @aws-amplify/deployed-backend-client: 1.4.0
  @aws-amplify/form-generator: 1.0.1
  @aws-amplify/model-generator: 1.0.5
  @aws-amplify/platform-core: 1.0.7
  @aws-amplify/plugin-types: 1.2.1
  @aws-amplify/sandbox: 1.2.0
  @aws-amplify/schema-generator: 1.2.1
  aws-amplify: 6.5.3
  aws-cdk: 2.155.0
  aws-cdk-lib: 2.155.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

Describe the bug

when generating client code for dart, using this command:

npx ampx generate graphql-client-code --format modelgen --model-target dart --out ./lib/models

The generated code consistently has this error:

Missing concrete implementation of 'ModelProviderInterface.getModelTypeByModelName'.
Try implementing the missing method, or make the class abstract.

My theory is its because I haven't declared any models in my data resource as I've used mongo and single table design. This is not the standard way and doesn't follow the documentation but is perfectly valid.

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 'Conversation.dart';
import 'Message.dart';
import 'MongoUser.dart';
import 'MongoUserLocation.dart';
import 'ProfileMedia.dart';
import 'UserProfile.dart';

export 'Conversation.dart';
export 'Message.dart';
export 'MongoUser.dart';
export 'MongoUserLocation.dart';
export 'ProfileMedia.dart';
export 'ProfileMediaType.dart';
export 'UserProfile.dart';

class ModelProvider implements amplify_core.ModelProviderInterface {
  @override
  String version = "65c55a040a40c5530448407cc0ee4f94";
  @override
  List<amplify_core.ModelSchema> modelSchemas = [];
  @override
  List<amplify_core.ModelSchema> customTypeSchemas = [Conversation.schema, Message.schema, MongoUser.schema, MongoUserLocation.schema, ProfileMedia.schema, UserProfile.schema];
  static final ModelProvider _instance = ModelProvider();

  static ModelProvider get instance => _instance;
}

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

  final T value;
}

Reproduction steps

For more context, here is my data/resource.ts :

import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
import { getMessagesByConversationId } from '../functions/getMessagesByConversationId/resource';
import { sendMessage } from '../functions/sendMessage/resource';
import { swipe } from '../functions/swipe/resource';
import { getConversationsByUserId } from '../functions/getConversationsByUserId/resource';
import { updateLocation } from '../functions/updateLocation/resource';
import { getMatchableProfiles } from '../functions/getMatchableProfiles/resource';
import { updateProfile } from '../functions/updateProfile/resource';
import { getProfile } from '../functions/getProfile/resource';

const schema = a.schema({
  Message: a.customType({
    message: a.string(),
    userId: a.string(),
    timestamp: a.timestamp(),
  }),
  Conversation: a.customType({
    latestMessage: a.string(),
    lastUpdated: a.timestamp(),
    members: a.json().required(),  // Members stored as JSON
  }),
  MongoUser: a.customType({
    name: a.string().required(),
    location: a.customType({
      type: a.string().required(),
      coordinates: a.float().array().required(),
    }),
    birthdate: a.integer().required(),
    media: a.ref('ProfileMedia').array().required(),
  }),
  ProfileMedia: a.customType({
    url: a.string(),
    type: a.enum(['picture', 'video', 'audio', 'text']),
    prompt: a.string(),
    body: a.string(),
  }),
  UserProfile: a.customType({
    name: a.string(),
    distance: a.float(),
    userId: a.string(),
    age: a.integer(),
    media: a.ref('ProfileMedia').array(),
  }),
  getMessagesByConversationId: a
    .query()
    .arguments({
      conversationId: a.string().required(),
      lastKey: a.string(),
    })
    .returns(a.ref('Message').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(),
      recipients: a.string().array().required()
    })
    .returns(a.ref('Message'))
    .handler(a.handler.function(sendMessage))
    .authorization(allow => [allow.publicApiKey()]),
  swipe: a
    .mutation()
    .arguments({
      userId: a.string().required(),
      targetUserId: a.string().required(),
      swipeRight: a.boolean().required(),
    })
    .returns(a.ref('Conversation'))
    .handler(a.handler.function(swipe))
    .authorization(allow => [allow.publicApiKey()]),
  getConversationsByUserId: a
    .query()
    .arguments({
      userId: a.string().required(),
    })
    .returns(a.ref('Conversation').array())
    .handler(a.handler.function(getConversationsByUserId))
    .authorization(allow => [allow.publicApiKey()]),
  updateLocation: a
    .mutation()
    .arguments({
      userId: a.string().required(),
      latitude: a.float().required(),
      longitude: a.float().required(),
    })
    .returns(a.string())
    .handler(a.handler.function(updateLocation))
    .authorization(allow => [allow.publicApiKey()]),
  getMatchableProfiles: a
    .query()
    .arguments({
      userId: a.string().required(),
      latitude: a.float().required(),
      longitude: a.float().required(),
      kmRadius: a.float().required(),
    })
    .returns(a.ref('UserProfile').array())
    .handler(a.handler.function(getMatchableProfiles))
    .authorization(allow => [allow.publicApiKey()]),
  updateProfile: a
    .mutation()
    .arguments({
      userId: a.string().required(),
      media: a.json().array(),
    })
    .returns(a.ref('UserProfile'))
    .handler(a.handler.function(updateProfile))
    .authorization(allow => [allow.publicApiKey()]),
  getProfile: a
    .query()
    .arguments({
      userId: a.string().required(),
    })
    .returns(a.ref('UserProfile'))
    .handler(a.handler.function(getProfile))
    .authorization(allow => [allow.publicApiKey()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'apiKey',
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
  functions: {
    getMessagesByConversationId,
    sendMessage,
    swipe,
    getConversationsByUserId,
    updateLocation,
    getMatchableProfiles,
    updateProfile,
    getProfile
  },
});
ykethan commented 2 months ago

Hey, thanks for raising this! I'm going to transfer this over to our codegen repository for better assistance.

dpilch commented 1 month ago

I wasn't able to reproduce this error. Your codegen packages may be out of date. Can you run the following and share the results?

npm list @aws-amplify/graphql-generator @aws-amplify/appsync-modelgen-plugin

npm update should bring you to the latest available version.