aws-amplify / amplify-codegen

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

Gen2 Data allow.ownersDefinedIn("owners") does not generate "owners" field on Swift model #845

Closed lawmicha closed 1 month ago

lawmicha commented 1 month ago

From the docs https://docs.amplify.aws/swift/build-a-backend/data/customize-authz/multi-user-data-access/#add-multi-user-authorization-rule

I'm using the schema

Todo15: a
      .model({
        content: a.string(),
    }).authorization(allow => [allow.ownersDefinedIn('owners')]),

In the TS code snippet, there's owners field array is updated with otherUserId.

// Add another user as an owner
await client.models.Todo.update(
  {
    id: newTodo.id,
    owners: [...(newTodo.owners as string[]), otherUserId],
  },

I can't do that the Swift generated models since there is no owner field exposed. The Swift generated models

Todo15.swift

// swiftlint:disable all
import Amplify
import Foundation

public struct Todo15: Model {
  public let id: String
  public var content: String?
  public var createdAt: Temporal.DateTime?
  public var updatedAt: Temporal.DateTime?

  public init(id: String = UUID().uuidString,
      content: String? = nil) {
    self.init(id: id,
      content: content,
      createdAt: nil,
      updatedAt: nil)
  }
  internal init(id: String = UUID().uuidString,
      content: String? = nil,
      createdAt: Temporal.DateTime? = nil,
      updatedAt: Temporal.DateTime? = nil) {
      self.id = id
      self.content = content
      self.createdAt = createdAt
      self.updatedAt = updatedAt
  }
}

Todo15+Schema.swift

// swiftlint:disable all
import Amplify
import Foundation

extension Todo15 {
  // MARK: - CodingKeys 
   public enum CodingKeys: String, ModelKey {
    case id
    case content
    case createdAt
    case updatedAt
  }

  public static let keys = CodingKeys.self
  //  MARK: - ModelSchema 

  public static let schema = defineSchema { model in
    let todo15 = Todo15.keys

    model.authRules = [
      rule(allow: .owner, ownerField: "owners", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read])
    ]

    model.listPluralName = "Todo15s"
    model.syncPluralName = "Todo15s"

    model.attributes(
      .primaryKey(fields: [todo15.id])
    )

    model.fields(
      .field(todo15.id, is: .required, ofType: .string),
      .field(todo15.content, is: .optional, ofType: .string),
      .field(todo15.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
      .field(todo15.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
    )
    }
    public class Path: ModelPath<Todo15> { }

    public static var rootPath: PropertyContainerPath? { Path() }
}

extension Todo15: ModelIdentifiable {
  public typealias IdentifierFormat = ModelIdentifierFormat.Default
  public typealias IdentifierProtocol = DefaultModelIdentifier<Self>
}
extension ModelPath where ModelType == Todo15 {
  public var id: FieldPath<String>   {
      string("id") 
    }
  public var content: FieldPath<String>   {
      string("content") 
    }
  public var createdAt: FieldPath<Temporal.DateTime>   {
      datetime("createdAt") 
    }
  public var updatedAt: FieldPath<Temporal.DateTime>   {
      datetime("updatedAt") 
    }
}

Package.json for Gen2 versions


  "devDependencies": {
    "@aws-amplify/backend": "^1.0.1",
    "@aws-amplify/backend-cli": "^1.0.1",
    "aws-cdk": "^2.139.0",
    "aws-cdk-lib": "^2.139.0",
    "constructs": "^10.3.0",
    "esbuild": "^0.20.2",
    "tsx": "^4.7.3",
    "typescript": "^5.4.5"
  },
  "dependencies": {
    "aws-amplify": "^6.2.0"
  }
lawmicha commented 1 month ago

In the subsequent example, https://docs.amplify.aws/swift/build-a-backend/data/customize-authz/multi-user-data-access/#override-to-a-list-of-owners

we explicitly override the owners field array as "authors"

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
      authors: a.string().array(), // record owner information now stored in "authors" field
    })
    .authorization(allow => [allow.ownersDefinedIn('authors')]),
});

Maybe In the first example, as a workaround, I can do this:

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
      owners: a.string().array(), // record owner information now stored in "authors" field
    })
    .authorization(allow => [allow.ownersDefinedIn('owners')]),
});
dpilch commented 1 month ago

Looks like this may be a data-schema issue or an existing issue also affecting Gen 1. The transformation for the schema:

a
  .model({
    content: a.string(),
}).authorization(allow => [allow.ownersDefinedIn('owners')]),

resolves to a GraphQL schema:

type Todo @model @auth(rules: [{allow: owner, ownerField: "owners"}])
{
  content: String
}

The owners field is not present.

So either:

  1. Data schema should be adding the field explicitly.
  2. Codegen should add the implicit owners field.
dpilch commented 1 month ago

Ok, got some clarification. This is a docs issue. The customer needs to explicitly add the owner field to the schema. owners: a.string().array().

lawmicha commented 1 month ago

Got it, this is the schema i'm using my test. I can update the docs with the explicit field

Todo15: a
      .model({
        content: a.string(),
        owners: a.string().array(),
    }).authorization(allow => [allow.ownersDefinedIn('owners')]),
phani-srikar commented 1 month ago

Thanks Michael!

github-actions[bot] commented 1 month ago

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.