0
votes

What I have here is my Account table and my settings table. Basically, there are many settings to one account and I plan on being able to input those settings with GraphQL using relay. Unfortunately, it throws the following error during yarn

relay:
ERROR:
GraphQLParser: Unknown field `maxAllowedRecords` on type `AccountObject`. Source: document `CreateAccountMutation` file: `mutations/CreateAccountMutation.js`.

maxAllowedRecords is not a field in Account object but rather a setting in the settings table that is related to a particular account object. I have the SQLAlchemy below:

class Account(Base):
    __tablename__ = 'accounts'

    id = Column(Integer, primary_key=True)
    raw_id = Column('id', Integer, primary_key=True)
    name = Column(String(255), nullable=False)
    num_licenses = Column(Integer, nullable=False)
    import_records_limit = Column(Integer, nullable=False)
    max_failed_logins = Column(Integer, nullable=False)
    status = Column(Integer, nullable=False)
    employee_lists = relationship('EmployeeList')
    users = relationship('User')
    policies = relationship("Policy", secondary=accounts_policies, back_populates="accounts")
    settings = relationship('AccountSetting')

class AccountSetting(Base):
    __tablename__ = 'account_settings'

    id = Column(Integer, primary_key=True)
    account = relationship('Account')
    account_id = Column(Integer, ForeignKey('accounts.id'))
    name = Column(String(30))
    value = Column(String(1000))

Here is the account mutation method

class CreateAccountMutation(graphene.Mutation):
    class Arguments:
        name = graphene.String(required=True)
        num_licenses = graphene.Int(required=True)
        import_records_limit = graphene.Int(required=True)
        status = graphene.Int(required=True)
        max_failed_logins = graphene.Int(required=True)
        account_type = graphene.String(required=False)
        max_allowed_records = graphene.String(required=False)
        password_expiry = graphene.String(required=False)
        account_type = graphene.String(required=False)
        reinstated_notifications = graphene.String(required=False)
        saml_only = graphene.String(required=False)
        access_ip_range = graphene.String(required=False)

    account = graphene.Field(lambda: AccountObject)

    def mutate(self, info, **kwargs):
        settings = []
        for name, value in kwargs.items():
            if name not in baseproperties:
                settings.append(AccountSetting(name=name,value=value))

        account = Account(name=kwargs.get('name'), num_licenses=kwargs.get('num_licenses'),
                import_records_limit=kwargs.get('import_records_limit'), status=kwargs.get('status'),
                max_failed_logins=kwargs.get('max_failed_logins'), settings=settings, date_created=datetime.utcnow())
        db_session.add(account)
        db_session.commit()
        return CreateAccountMutation(account=account)

As you can see here, the additional properties passed that are not in the base properties will be placed in the settings as AccountSetting objects. This works with graphiQL:

mutation {
  createAccount(name: "Red Cross", numLicenses: 76, importRecordsLimit: 50000, maxFailedLogins: 20, status: 1, maxAllowedRecords: "50", passwordExpiry: "90", accountType: "fullservice", reinstatedNotifications: "1", samlOnly: "0", accessIpRange: "Test Rangate") {
    account {
      name
      settings {
        name
        value
      }
    }
  }
}

But this mutation file doesn't compile:

const mutation =  graphql`
    mutation CreateAccountMutation(
        $name: String!, $numLicenses: Int!, $importRecordsLimit: Int!, $status: Int! 
        $maxFailedLogins: Int!, $maxAllowedRecords: String!, $passwordExpiry: String!,
        $accountType: String!, $reinstatedNotifications: String!, $samlOnly: String!, $accessIpRange: String!

        ) {
        createAccount(  name: $name, numLicenses: $numLicenses, importRecordsLimit: $importRecordsLimit, status: $status,
                        maxFailedLogins: $maxFailedLogins, maxAllowedRecords: $maxAllowedRecords, passwordExpiry: $passwordExpiry,
                        accountType: $accountType, reinstatedNotifications: $reinstatedNotifications, samlOnly: $samlOnly, accessIpRange: $accessIpRange
                    ) {
            account {
                name
                numLicenses
                importRecordsLimit
                status
                maxFailedLogins
                maxAllowedRecords
                passwordExpiry
                accountType
                reinstatedNotifications
                samlOnly
                accessIpRange
            }
        }
    }
`;

export default (
    name,num_licenses,import_records_limit,status,
    maxFailedLogins,maxAllowedRecords,passwordExpiry,
    accountType,reinstatedNotifications,samlOnly,
    accessIpRange,  callback) => {

    const variables = {
        name,
        num_licenses,
        import_records_limit,
        status,
        maxFailedLogins,
        maxAllowedRecords,
        passwordExpiry,
        accountType,
        reinstatedNotifications,
        samlOnly,
        accessIpRange,
    }

    commitMutation(
        environment,
        {
            mutation,
            variables,
            onCompleted: () => {
                callback()
            },
            onError: err => console.error(err),
        },
    )
}

Am I doing something wrong here? Is there another way? A 'correct' way to approach this problem?

1

1 Answers

0
votes

So What I did here (not sure if this is the correct way) is that I added the additional columns in the Account Object despite not actually having those columns in the database. The compilation got thru but I am not sure if it will fully work:

class Account(Base):
    __tablename__ = 'accounts'

    id = Column(Integer, primary_key=True)
    raw_id = Column('id', Integer, primary_key=True)
    name = Column(String(255), nullable=False)
    num_licenses = Column(Integer, nullable=False)
    import_records_limit = Column(Integer, nullable=False)
    max_failed_logins = Column(Integer, nullable=False)
    status = Column(Integer, nullable=False)
    employee_lists = relationship('EmployeeList')
    users = relationship('User')
    policies = relationship("Policy", secondary=accounts_policies, back_populates="accounts")
    settings = relationship('AccountSetting')
    date_created = Column(DateTime)
    account_type = Column(String(255), nullable=False)
    max_allowed_records = Column(String(255), nullable=False)
    password_expiry = Column(String(255), nullable=False)
    account_type = Column(String(255), nullable=False)
    reinstated_notifications = Column(String(255), nullable=False)
    saml_only = Column(String(255), nullable=False)
    access_ip_range = Column(String(255), nullable=False)

Edit: Doesn't work getting the following error:

"errors": [
    {
      "message": "(pymysql.err.InternalError) (1054, \"Unknown column 'max_allowed_records' in 'field list'\")\n[SQL: INSERT INTO accounts (name, num_licenses, import_records_limit, max_failed_logins, status, date_created, max_allowed_records, password_expiry, account_type, reinstated_notifications, saml_only, access_ip_range) VALUES (%(name)s, %(num_licenses)s, %(import_records_limit)s, %(max_failed_logins)s, %(status)s, %(date_created)s, %(max_allowed_records)s, %(password_expiry)s, %(account_type)s, %(reinstated_notifications)s, %(saml_only)s, %(access_ip_range)s)]\n[parameters: {'name': 'Blue Green', 'num_licenses': 100, 'import_records_limit': 5000, 'max_failed_logins': 20, 'status': 1, 'date_created': datetime.datetime(2019, 9, 3, 13, 24, 2, 354409), 'max_allowed_records': None, 'password_expiry': None, 'account_type': None, 'reinstated_notifications': None, 'saml_only': None, 'access_ip_range': None}]\n(Background on this error at: http://sqlalche.me/e/2j85)"