Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple one2one/one2many/many2many relationships will not compile SWIFT #3946

Open
bargeboss-chris opened this issue Jan 6, 2025 · 5 comments
Labels
api Issues related to the API category bug Something isn't working pending-maintainer-response Issue is pending response from an Amplify team member

Comments

@bargeboss-chris
Copy link

Describe the bug

If you have a singular o2o/o2m/m2m relationship in a table the swift generated files by amplify will compile and run correctly. If there are multiple o2o/o2m/m2m relationships in a table that reference multiple different tables, the files generated will not compile.

Steps To Reproduce

Amplify generation 2

Xcode 16.2

used our code for our application as well as followed the tutorial in the documentation.  this reproduced on both scenarios.

Expected behavior

compile correctly.

Amplify Framework Version

6.12.0

Amplify Categories

DataStore

Dependency manager

Swift PM

Swift version

5

CLI version

1.4.6

Xcode version

16.2

Relevant log output

<details>
<summary>Log Messages</summary>


INSERT LOG MESSAGES HERE
```

Is this a regression?

No

Regression additional context

No response

Platforms

iOS

OS Version

iOS 18.2

Device

iPhone 16 pro

Specific to simulators

No response

Additional context

No response

@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify team member labels Jan 6, 2025
@bargeboss-chris
Copy link
Author

Screenshot 2025-01-06 at 2 33 52 AM Screenshot 2025-01-06 at 2 33 40 AM Screenshot 2025-01-06 at 2 30 12 AM Screenshot 2025-01-06 at 2 29 50 AM Screenshot 2025-01-06 at 2 29 29 AM

@mattcreaser
Copy link
Member

Thanks for filing the issue @bargeboss-chris. Could you please provide the plain-text version the schema that causes the issue? That will help us reproduce and diagnose the issue.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Jan 6, 2025
@mattcreaser mattcreaser added bug Something isn't working api Issues related to the API category and removed pending-triage Issue is pending triage labels Jan 6, 2025
@bargeboss-chris
Copy link
Author

Hi @mattcreaser

Here is the file

import { type ClientSchema, a, defineData } from '@aws-amplify/backend';

const schema = a.schema({

customer: a.model({
customerName: a.string().required(),
address1: a.string().required(),
address2: a.string(),
city: a.string().required(),
state: a.string().required(),
zipCode: a.integer().required(),
country: a.string().required(),
billingEmail: a.email().required(),
billingName: a.string().required(),
billingPhone: a.string().required(),
creationTimeStamp: a.timestamp(),
usr: a.hasMany('usr', 'customerID'),
resource: a.hasMany('resource', 'customerID'),//many should be
trackerHardware: a.hasMany('trackerHardware', 'customerID'),
grp: a.hasMany('grp','customerID'),
})
.secondaryIndexes((index) => [index("customerName")])
.authorization(allow => [allow.publicApiKey()]),

usr: a.model({
cognitoUserID: a.string().required(),
givenName: a.string().required(),
familyName: a.string().required(),
phoneNumber: a.phone().required(),
permissionLevel: a.integer().required(),
enabled: a.boolean().required(),
chartsEnabled: a.boolean().required(),
lastSignIn: a.timestamp().required(),
email: a.email().required(),
customerID: a.id(),
customer: a.belongsTo('customer', 'customerID'),
simCardAssignmentHistoryID: a.id(),
simCardAssignmentHistory: a.belongsTo('simCardAssignmentHistory','simCardAssignmentHistoryID'),
trackerHardwareServiceID: a.id(),
trackerHardwareService: a.belongsTo('trackerHardwareService','trackerHardwareServiceID'),
trackerHardwareAssignmentHistoryID: a.id(),
trackerHardwareAssignmentHistory: a.belongsTo('trackerHardwareAssignmentHistory','trackerHardwareAssignmentHistoryID'),
simCardID: a.id(),
simCard: a.belongsTo('simCard','simCardID'),
resourceGeoFenceAlarmHistoryID: a.id(),
resourceGeoFenceAlarmHistory: a.belongsTo('resourceGeoFenceAlarmHistory','resourceGeoFenceAlarmHistoryID'),
resourceGeoFenceActivationHistoryEnableID: a.id(),
resourceGeoFenceActivationHistoryEnable: a.belongsTo('resourceGeoFenceActivationHistory','resourceGeoFenceActivationHistoryEnableID'),
resourceGeoFenceActivationHistoryDisableID: a.id(),
resourceGeoFenceActivationHistoryDisable: a.belongsTo('resourceGeoFenceActivationHistory','resourceGeoFenceActivationHistoryDisableID'),
resourceLowBatteryAlarmHistoryID: a.id(),
resourceLowBatteryAlarmHistory: a.belongsTo('resourceLowBatteryAlarmHistory','resourceLowBatteryAlarmHistoryID'),
resourceHardHitAlarmHistoryID: a.id(),
resourceHardHitAlarmHistory: a.belongsTo('resourceHardHitAlarmHistory','resourceHardHitAlarmHistoryID'),
grp: a.hasMany('grpUsr','usrID'),
})
.secondaryIndexes((index) => [index("cognitoUserID")])
.authorization(allow => [allow.publicApiKey()]),

grp: a.model({
name: a.string().required(),
grpDescription: a.string().required(),
grpNotes: a.string(),
enabled: a.boolean().required(),
chartsEnabled: a.boolean().required(),
lastSignIn: a.timestamp().required(),
customerID: a.id(),
customer: a.belongsTo('customer', 'customerID'),
resource: a.hasMany('grpResource','grpID'),
usr: a.hasMany('grpUsr','grpID'),
})
.authorization(allow => [allow.publicApiKey()]),

grpUsr: a.model({
grpID: a.id().required(),
grp: a.belongsTo('grp','grpID'),
usrID: a.id().required(),
usr: a.belongsTo('usr','usrID'),
})
.authorization(allow => [allow.publicApiKey()]),

resource: a.model({
name: a.string().required(),
hullNumber: a.string(),
MMSI: a.string(),
magHdgCorrection: a.float(),
type: a.string().required(),
yearBuilt: a.integer(),
beam: a.float(),
draft: a.float(),
lenth: a.float(),
overallHeight: a.float(),
distFromPort: a.float(),
distFromBow: a.float(),
icon: a.string(),//123
iconColor: a.string(),
GFAlarmActive: a.boolean(),
GFActive: a.boolean(),
GFLatCtr: a.float(),
GFLonCtr: a.float(),
GFRad: a.float(),
HHThreshold: a.float(),
HHAlarmActive: a.boolean(),
HHXg: a.float(),
HHYg: a.float(),
HHZg: a.float(),
mqttRate: a.integer(),
BattLowVoltsSP: a.float(),
LowBattActive1: a.boolean(),
LowBattActive2: a.boolean(),
LowBattActive3: a.boolean(),
LowBattActive4: a.boolean(),
customerID: a.id(),
customer: a.belongsTo('customer', 'customerID'),
trackerHardwareAssignmentHistoryID: a.id(),
trackerHardwareAssignmentHistory: a.belongsTo('trackerHardwareAssignmentHistory','trackerHardwareAssignmentHistoryID'),
grp: a.hasMany('grpResource','resourceID'),
trackerHardware: a.hasOne('trackerHardware','resourceID'),
resourceLocationHistory: a.hasMany('resourceLocationHistory','resourceID'),
resourceGeoFenceAlarmHistory: a.hasMany('resourceGeoFenceAlarmHistory','resourceID'),
resourceGeoFenceActivationHistory: a.hasMany('resourceGeoFenceActivationHistory','resourceID'),
resourceHardHitAlarmHistory: a.hasMany('resourceHardHitAlarmHistory','resourceID'),
resourceLowBatteryAlarmHistory: a.hasMany('resourceLowBatteryAlarmHistory','resourceID')
})
.authorization(allow => [allow.publicApiKey()]),

grpResource: a.model({
grpID: a.id().required(),
grp: a.belongsTo('grp','grpID'),
resourceID: a.id().required(),
resource: a.belongsTo('resource','resourceID'),
})
.authorization(allow => [allow.publicApiKey()]),

trackerHardware: a.model({
SerialNumber: a.string(),
shipDate: a.date(),
warrantyExpireDate: a.date(),
unitType: a.string(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
customerID: a.id(),
customer: a.belongsTo('customer', 'customerID'),
simCardAssignmentHistoryID: a.id(),
simCardAssignmentHistory: a.belongsTo('simCardAssignmentHistory','simCardAssignmentHistoryID'),
simCard: a.hasOne('simCard','trackerHardwareID'),
resourceLocationHistory: a.hasMany('resourceLocationHistory','trackerHardwareID'),
trackerHardwareService: a.hasMany('trackerHardwareService','trackerHardwareID'),
})
.secondaryIndexes((index) => [index("SerialNumber")])
.authorization(allow => [allow.publicApiKey()]),

trackerHardwareAssignmentHistory: a.model({
resource: a.hasOne('resource','trackerHardwareAssignmentHistoryID'),
usr: a.hasOne('usr','trackerHardwareAssignmentHistoryID'),
})
.authorization(allow => [allow.publicApiKey()]),

trackerHardwareService: a.model({
dateReceived: a.date(),
dateShipped: a.date(),
shippingCarrier: a.string(),
trackingNumber: a.string(),
serviceDate: a.datetime(),
serviceNotes: a.string(),
trackerHardwareID: a.id(),
trackerHardware: a.belongsTo('trackerHardware','trackerHardwareID'),
trackerHardwareServiceParts: a.hasMany('trackerHardwareServiceParts','trackerHardwareServiceID'),
usr: a.hasOne('usr','trackerHardwareServiceID'),
})
.authorization(allow => [allow.publicApiKey()]),

trackerHardwareServiceParts: a.model({
item: a.string().required(),
cost: a.float().required(),
sellPrice: a.float().required(),
vendor: a.string().required(),
vendorPartNumber: a.string().required(),
notes: a.string(),
trackerHardwareServiceID: a.id(),
trackerHardwareService: a.belongsTo('trackerHardwareService','trackerHardwareServiceID'),
})
.authorization(allow => [allow.publicApiKey()]),

simCard: a.model({
SSID: a.string().required(),
starlinkActive: a.boolean(),
carrier: a.string().required(),
phoneNumber: a.phone().required(),
trackerHardwareID: a.id(),
trackerHardware: a.belongsTo('trackerHardware','trackerHardwareID'),
usr: a.hasOne('usr','simCardID'),
})
.secondaryIndexes((index) => [index("SSID")])
.authorization(allow => [allow.publicApiKey()]),

simCardAssignmentHistory: a.model({
trackerHardware: a.hasOne('trackerHardware','simCardAssignmentHistoryID'),
usr: a.hasOne('usr','simCardAssignmentHistoryID'),
})
.authorization(allow => [allow.publicApiKey()]),

resourceLocationHistory: a.model({
timeStamp: a.timestamp(),
Batt1Volts: a.float(),
Batt2Volts: a.float(),
Batt3Volts: a.float(),
Batt4Volts: a.float(),
cellSig: a.integer(),
distanceFromLastLocation: a.float(),
GFDeviation: a.float(),
gX: a.float(),
gY: a.float(),
gZ: a.float(),
IMEI: a.float(),
insertTimeStamp: a.timestamp(),
lat: a.float(),
lon: a.float(),
magneticHeading: a.float(),
mileMarker: a.float(),
numSat: a.integer(),
riverName: a.string(),
speed: a.float(),
xM: a.float(),
yM: a.float(),
zM: a.float(),
TTL: a.timestamp(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
trackerHardwareID: a.id(),
trackerHardware: a.belongsTo('trackerHardware','trackerHardwareID'),
})
.secondaryIndexes((index) => [
index("resourceID")
.sortKeys(["timeStamp"]),
])
.authorization(allow => [allow.publicApiKey()]),

resourceGeoFenceAlarmHistory: a.model({
enabledTimeStamp: a.timestamp().required(),
clearedTimeStamp: a.timestamp().required(),
lat: a.float(),
lon: a.float(),
speed: a.float(),
GFDeviation: a.float(),
GFLatCtr: a.float(),
GFLonCtr: a.float(),
GFRad: a.float(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
usrCleared: a.hasOne('usr','resourceGeoFenceAlarmHistoryID'),
})
.secondaryIndexes((index) => [
index("resourceID")
.sortKeys(["enabledTimeStamp"]),
])
.authorization(allow => [allow.publicApiKey()]),

resourceGeoFenceActivationHistory: a.model({
enabledTimeStamp: a.timestamp().required(),
disabledTimeStamp: a.timestamp().required(),
GFEnableLat: a.float(),
GFEnableLon: a.float(),
GFDisableLat: a.float(),
GFDisableLon: a.float(),
GFDisableSpeed: a.float(),
GFDisableDeviation: a.float(),
GFEnableLatCtr: a.float(),
GFEnableLonCtr: a.float(),
GFEnableRad: a.float(),
buttonEnable: a.boolean(),
buttonDisable: a.boolean(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
usrEnabled: a.hasOne('usr','resourceGeoFenceActivationHistoryEnableID'),
usrDisabled: a.hasOne('usr','resourceGeoFenceActivationHistoryDisableID')
})
.secondaryIndexes((index) => [
index("resourceID")
.sortKeys(["enabledTimeStamp"]),
])
.authorization(allow => [allow.publicApiKey()]),

resourceHardHitAlarmHistory: a.model({
enabledTimeStamp: a.timestamp().required(),
disabledTimeStamp: a.timestamp().required(),
gX: a.float(),
gY: a.float(),
gZ: a.float(),
threshold: a.float(),
lat: a.float(),
lon: a.float(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
usrCleared: a.hasOne('usr','resourceHardHitAlarmHistoryID'),
})
.secondaryIndexes((index) => [
index("resourceID")
.sortKeys(["enabledTimeStamp"]),
])
.authorization(allow => [allow.publicApiKey()]),

resourceLowBatteryAlarmHistory: a.model({
enabledTimeStamp: a.timestamp().required(),
disabledTimeStamp: a.timestamp().required(),
lat: a.float(),
lon: a.float(),
Batt1: a.boolean(),
Batt1Voltage: a.float(),
Batt2: a.boolean(),
Batt2Voltage: a.float(),
Batt3: a.boolean(),
Batt3Voltage: a.float(),
Batt4: a.boolean(),
Batt4Voltage: a.float(),
resourceID: a.id(),
resource: a.belongsTo('resource','resourceID'),
usrCleared: a.hasOne('usr','resourceLowBatteryAlarmHistoryID'),
})
.secondaryIndexes((index) => [
index("resourceID")
.sortKeys(["enabledTimeStamp"]),
])
.authorization(allow => [allow.publicApiKey()]),

});

export type Schema = ClientSchema;

export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'apiKey',
apiKeyAuthorizationMode: { expiresInDays: 30 }
},
});

/== STEP 1 ===============================================================
The section below creates a Todo database table with a "content" field. Try
adding a new "isDone" field as a boolean. The authorization rule below
specifies that any unauthenticated user can "create", "read", "update",
and "delete" any "Todo" records.
=========================================================================
/

/*== STEP 2 ===============================================================
Go to your frontend source code. From your client-side code, generate a
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
WORK IN THE FRONTEND CODE FILE.)

Using JavaScript or Next.js React Server Components, Middleware, Server
Actions or Pages Router? Review how to generate Data clients for those use
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/

/*
"use client"
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

const client = generateClient() // use this Data client for CRUDL requests
*/

/== STEP 3 ===============================================================
Fetch records from the database and use them in your frontend component.
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
=========================================================================
/

/* For example, in a React component, you can use this snippet in your
function's RETURN statement */
// const { data: todos } = await client.models.Todo.list()

// return

    {todos.map(todo =>
  • {todo.content}
  • )}

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Jan 6, 2025
@bargeboss-chris
Copy link
Author

@mattcreaser

here is the sample file I was using for testing. the one above is for our production application

import { type ClientSchema, a, defineData } from '@aws-amplify/backend';

/== STEP 1 ===============================================================
The section below creates a Todo database table with a "content" field. Try
adding a new "isDone" field as a boolean. The authorization rule below
specifies that any unauthenticated user can "create", "read", "update",
and "delete" any "Todo" records.
=========================================================================
/
const schema = a.schema({
TodoDB: a
.model({
content: a.string(),
isDone: a.boolean().required(),
tasksDBTodoDB: a.hasMany('TasksDB','todoIdTasksDB'),
dateIDTodoDB: a.id(),
datedb: a.belongsTo('TasksDB','dateIDTodoDB'),
TasksDB2: a.hasMany('TasksDB2','todoIdTasksDB2')
})
.authorization(allow => [allow.publicApiKey()]),

TasksDB: a
.model({
  content: a.string(),
  isDone: a.boolean().required(),
  todoIdTasksDB: a.id(),
  todoTasksDB: a.belongsTo('TodoDB', 'todoIdTasksDB'),
  TodoDBTasksDB: a.hasOne('TodoDB','dateIDTodoDB'),
})
.authorization(allow => [allow.publicApiKey()]),

TasksDB2: a
.model({
  content: a.string(),
  isDone: a.boolean().required(),
  todoIdTasksDB2: a.id(),
  todoTasksDB2: a.belongsTo('TodoDB', 'todoIdTasksDB2'),
})
.authorization(allow => [allow.publicApiKey()])

});

export type Schema = ClientSchema;

export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'apiKey',
apiKeyAuthorizationMode: { expiresInDays: 30 }
},
});

/*== STEP 2 ===============================================================
Go to your frontend source code. From your client-side code, generate a
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
WORK IN THE FRONTEND CODE FILE.)

Using JavaScript or Next.js React Server Components, Middleware, Server
Actions or Pages Router? Review how to generate Data clients for those use
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/

/*
"use client"
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

const client = generateClient() // use this Data client for CRUDL requests
*/

/== STEP 3 ===============================================================
Fetch records from the database and use them in your frontend component.
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
=========================================================================
/

/* For example, in a React component, you can use this snippet in your
function's RETURN statement */
// const { data: todos } = await client.models.Todo.list()

// return

    {todos.map(todo =>
  • {todo.content}
  • )}

@bargeboss-chris
Copy link
Author

@mattcreaser : Any updates on this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Issues related to the API category bug Something isn't working pending-maintainer-response Issue is pending response from an Amplify team member
Projects
None yet
Development

No branches or pull requests

2 participants