#Type '{ connect: { repository_id: number; }; }' is not assignable to type 'never'.ts(2322)

162 messages ยท Page 1 of 1 (latest)

warped onyx
#

It might be a noob question but what I am doing wrong here?

Type '{ connect: { repository_id: number; }; }' is not assignable to type 'never'.ts(2322)
stream-dependency-graph.ts(47, 11): The expected type comes from property 'repository' which is declared here on type '{ where: { repository_id_filename: { repository_id: number; filename: string; }; }; update: { actions: string[]; filename: string; }; create: { repository_id: number; repository_name: string; actions: string[]; filename: string; }; repository: never; }'

Here is my upsert function

  private async upsertEntityInDatabase(data: ActionDependencyList) {
    try{
      if(!data.actionsList){
        logger.info({repoName: data.repository_name}, "No dependency found for the repository");
        return;
      }

      for( const [file, actions] of data.actionsList) {
      logger.info({actions, file}, "Upserting Dependency entity");
        const dependencyEntity = await this.prisma.actionDependencyList.upsert({
          where: {
            repository_id_filename: {
              repository_id: data.repository_id,
              filename: file,
            },
          },
          update: {
            actions: actions,
            filename: file,
          },
          create: {
            repository_id: data.repository_id,
            repository_name: data.repository_name,
            actions: actions,
            filename: file,
            repository: {
              connect: { repository_id: data.repository_id },
            },
          },
        });
        logger.info({dependencyEntity}, "Upserted Dependency entity");
      }
    } catch (error) {
      logger.error(error, "Error upserting Dependency entity");
      throw error;
    }
  }
}
atomic dagger
#

Don't you think it might be a good idea to show the table schema?

warped onyx
#
model ActionDependencyList {
  repository_id   BigInt
  repository_name String
  filename        String
  actions         String[]
  repository      Repository @relation(fields: [repository_id], references: [repository_id])

  @@id([repository_id, filename])
}
atomic dagger
#

Cool

#

Do it work with prisma.actionDependencyList ? Honestly can't remember how we make rows with related rows

warped onyx
#

Sorry I could not get this part :/
Do it work with prisma.actionDependencyList

atomic dagger
#

I mean

#

prisma.actionDependencyList.create

warped onyx
#

But I have to use upsert

atomic dagger
#

I know, I'm wondering if it works for create, and it's an upsert thing

warped onyx
#

Then it ll probably complain with where

#

what might be the reason of undefined here

Types of property 'repository' are incompatible.
 Type '{ connect: { repository_id: number; }; }' is not assignable to type 'undefined'.ts(2322)
atomic dagger
#
this.prisma.actionDependencyList.create({
  data: {
    repository_id: data.repository_id,
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
    repository: {
      connect: { repository_id: data.repository_id },
    },
  },
});
#

Maybe you didn't generate?

warped onyx
#

I ran npx prisma generate command

atomic dagger
#

hmmm

#

Let me make a demo, unless you have one

#

Can I see your Repository schema?

#

Are you using MongoDB?

#

I think

#

You don't need

 repository: {
      connect: { repository_id: data.repository_id },
    },
#

This already creates the connection

repository_id: data.repository_id,
#

actionDependencyList points to repository with repository_id

#

What you are doing is for when repository points to actionDependencyList with actionDependencyList.repository_id

atomic dagger
#

@warped onyx

warped onyx
#

How can I do that?

#

I am using PostgreSQL @atomic dagger

#

I think you are right

atomic dagger
#

All you need to do is remove the reposity entry

warped onyx
#

this seems redundant

repository_id: data.repository_id,
atomic dagger
#

That bit you keep, right?

warped onyx
#

yeah error gone

  create: {
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
    repository: {
      connect: { repository_id: data.repository_id },
    },
  },
});
#

But what is the purpose of connect here then

    repository: {
      connect: { repository_id: data.repository_id },
    },
#

I meant vs below

  create: {
    repository_id: data.repository_id,
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
  },
warped onyx
#

Error was gone for both of them

atomic dagger
#

Ah

#

well they both do the same thing right? They just set the repository_id.

warped onyx
#

This is something I don't know or did not understand ๐Ÿ™‚

atomic dagger
#

Do you understand now?

warped onyx
#

I think connect is more to set foreign key

atomic dagger
#

Yeah I think if you need to update the other table, then you need connect.

#

I haven't used connect/create much to be honest.

warped onyx
#

I also did not fully understand

#

Update other table means, will it update also Repository table in this case?

atomic dagger
#

If you have
Users and Posts, and a User can have many Posts

#

then Posts will have user_id

#

Users can't have a post_ids array, because we don't make relational DBs that way, it's not easy to work with

#

So if you want to create a User and then connect 1 or more posts, you need to make a User, then modify Post.user_ids

#

So here you need some way to say that you want to modify a second table

#

But when you make a Post, you don't modify User. Just set user_id in Post

warped onyx
#

Gothca, so in my scenario when there will be a new Repository, the repository_id in the ActionsDependecy table will be updated as well, right?

atomic dagger
#

If you make a new Repository and you want ActionsDependecy to reference it, then you could use a connect to update that, I think.

#

I think that's an unusual data pattern though.

#

Normally ActionDepencyList would always reference a single Repository. Unless it was shared between multiple Repos.

warped onyx
#

This is not something I want and where I also confused

#

One Repository can have multiple Actions

#

So what will be the benefit of having a relationg here or using connect method?
This is asked from one of my team member where i really confused

model ActionDependencyList {
  repository_id   BigInt
  repository_name String
  filename        String
  actions         String[]
  repository      Repository @relation(fields: [repository_id], references: [repository_id])

  @@id([repository_id, filename])
}
atomic dagger
#

So a Repo can have multiple actions

warped onyx
#

Yes

atomic dagger
#

In relational database theory, you should not have a list, but multiple rows

#
model ActionDependency {
  repository_id   BigInt
  repository_name String
  filename        String
  action         String
  repository      Repository @relation(fields: [repository_id], references: [repository_id])

  @@id([repository_id, filename])
}
#

so a single action per record

warped onyx
#

the problem, what happens if one of the action deleted, or change?

atomic dagger
#

and in Repository you have
ActionDepencies ActionDependency[]

atomic dagger
warped onyx
#

But how I do understand that

#

Let me give you an example

atomic dagger
#
Repository (id, name)
id: 1, name 'A'

RepositoryAction (id, repository_id, action)
id: 1, repository_id: 1, action: 'checkout:v4'
id: 2, repository_id: 1, action: 'setup-python:v3'
#

So if you want to remove repository 1 'delete' you find the RepositoryAction with repository_id of 1 and action of 'delete'

#

If you have action: String[] it's hard to remove an action. You have to get the list, then filter the list in JS, then save.

#

With the relational style I described, you can do it all with DB queries.

warped onyx
#

Let me give you concrete example

atomic dagger
#

yep

warped onyx
#

So lets say we have one repository.
repositoryA can have multiple actions something like [actions/checkout:v4, actions/setup-python:v3]

atomic dagger
#

Yep?

warped onyx
#

Lets say, I recorded this to database like below

repository_id repository_name. actions
1 repositoryA actions/checkout:v4
1 repositoryA actions/setup-python

#

I am collecting the data from each repository daily and lets say tomorrow one of the actions change to actions/checkout:v5

atomic dagger
#

That is not correct. RespositoryA has repository_id 1

#

it can't have more than one ID

warped onyx
#

yeah sorry for the typo

atomic dagger
#

You should not store multiple peices of information about the Repository in RepositoryActions. If you do, then when you change the name of RepositoryA, you have to change that information in many places

#

And you can end up with inconsistent data.

#

So only reference the repository_id here

#
Repository (id, name)
id: 1, name 'RepositoryA'

RepositoryAction (id, repository_id, action)
id: 1, repository_id: 1, action: 'checkout:v4'
id: 2, repository_id: 1, action: 'setup-python:v3'
#

You should have this structure

warped onyx
#

But what happens if checkout changed to checkout:v5 ?

#

I need to update that record

atomic dagger
#

Right, so you can update that row, or make a new rows, it depends

warped onyx
#

I do not want new row, because in that case, when someone call API it will return old action

#

But it's not valid or active anymore in that repository

atomic dagger
#

if you don't need history you can just do

prisma.repositoryAction.update({
    where: { repository_id: 1, action: 'checkout:v4' },
    data: { action: 'checkout:v5' }
})
#

Sometimes this is a problem, because you've deleted information about the history of the database. Maybe some task started yesterday, and it expects action to still be v4. In this case use a status to disable the old action and add a new row.

#
Repository (id, name)
id: 1, name 'RepositoryA'

RepositoryAction (id, repository_id, action, status)
id: 1, repository_id: 1, action: 'checkout:v4', status: 'active'
id: 2, repository_id: 1, action: 'setup-python:v3', status: 'active'
#

update to:

RepositoryAction (id, repository_id, action, status)
id: 1, repository_id: 1, action: 'checkout:v4', status: 'inactive'
id: 2, repository_id: 1, action: 'setup-python:v3', status: 'active'
id: 3, repository_id: 1, action: 'checkout:v5', status: 'active'
warped onyx
#

or maybe timestamp rather status

atomic dagger
#

You can do deleted_at: timestamp | null

#

If you want to know when it was made inactive

#

If the Actions need to be ordered, that could be an issue.

warped onyx
#

But what is wrong to store string list in database.. smth like
repository_id repository_name. actions
1 repositoryA [actions/checkout:v4, actions/setup-python:v3]

#

In this case, I just need to update the list

atomic dagger
#

You can do that. But it reduces what you can do at the database level

#

For example you cannot write a simple SQL query to replace actions/checkout:v4 with actions/checkout:v5, so you need to use a programming language to help

#

And it is difficult to write a query to see whether repo 1 has a 'actions/checkout:v4' action.

#

Again you probably will access the record, then have to check it with JS.

warped onyx
#

I got your point now

#

But i don't think i ll have this use case where we need to manually change smth in database

atomic dagger
#

There are rules about database structure that optimise what the database can do. Of course, sometimes rules can be broken too.

warped onyx
#

So silly question ๐Ÿ˜„

#

Do I still need to use connect, if I store list

atomic dagger
#

Well, these practices were created by people who studied databases. They are designed around what works with SQL.

#

So when you have a programming language you can do things differently. But in some situations it's inefficient.

#

So far in your use case I don't see a problem with your approach. But I think I would follow the relational database standard just so I don't run into problems some day.

warped onyx
#

thanks a lot @atomic dagger

atomic dagger
#

No probs

warped onyx
#

Sorry for the dump question again and again

#

But do we really need connect here then

atomic dagger
atomic dagger
#

But I also suggest you learn about relational database standards and normalization.

warped onyx
#

Will definetely watch

atomic dagger
#

It's a standard approach, so it makes it easy when everyone follows a similar approach

atomic dagger
#

So you're kind of cutting off some DB capabilities, and relying on a non standard approach

#

which seems more risky to me ๐Ÿ˜…

warped onyx
#

In my code I have Set, so I won't store duplicated actions

atomic dagger
#

Cool

warped onyx
#

So i still did not understand the benefit of connect here ๐Ÿ˜„

atomic dagger
#

We sometimes break the normalization rules for efficiency. For example if just need to pull a big list of values, without doing any logic, then a simple list is likely to be faster

atomic dagger
#
  create: {
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
    repository: {
      connect: { repository_id: data.repository_id },
    },
  },
  create: {
    repository_id: data.repository_id,
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
  },
#

these seem like 2 ways to write the same thing.

#

I would use the second one.

#
prisma.user.create({
  name: 'bob',
  posts: {
    create: [{ title: 'first post' }, { title: 'second post' }]
  }
}

^ If you want to make a user with 2 posts ^

prisma.user.create({
  name: 'bob',
  posts: {
    connect: [{ id: 1 }, { id: 2 }]
  }
}
#

^ If you want to make a user, and connect 2 posts that already exist to that user ^

#

It's very rare that you need connect

#

It's basically when you want to steal some sub items from something else ๐Ÿ™‚

warped onyx
#

Sorry probably i am silly

#

What happens if I don't use connect here for instance?

prisma.user.create({
  name: 'bob',
  posts: {
    connect: [{ id: 1 }, { id: 2 }]
  }
}
atomic dagger
#

Then Bob has no posts

warped onyx
#

so I remove connect here, repository won't have any actions, right ? ๐Ÿ™‚

  create: {
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
    repository: {
      connect: { repository_id: data.repository_id },
    },
  },
atomic dagger
#
await prisma.post.create({ data: {
  id: 1,
  title: 'first'
}})
await prisma.post.create({
  id: 2,
  title: 'second'
}})

await prisma.user.create({ data: {
  name: 'bob',
  posts: {
    connect: [{ id: 1 }, { id: 2 }]
  }
}})
atomic dagger
atomic dagger
warped onyx
#

I meant in this example

  create: {
    repository_name: data.repository_name,
    actions: actions,
    filename: file,
  },
atomic dagger
atomic dagger
warped onyx
#

If I did not specify connect or repository_id

atomic dagger
#

I think you can figure that out : )

warped onyx
#

so actions won't have any repository

#

like bob has not posts ๐Ÿ™‚

atomic dagger
#

of course

warped onyx
#

So one table for Repository
Another table for Actions

And another table for RepositoryActions?

atomic dagger
#

I didn't really suggest that. But you can have an Actions table yes.

#

If there are a limited set of Actions that are reused, and you need to store some data about those actions, that is a good approach.

warped onyx
#

Actually I have many-to-many relation here

atomic dagger
#

yep

warped onyx
#

1 repository can have multiple actions

#

1 action can have many repositories