Skip to content

Commit

Permalink
Merge pull request #82 from ynput/enhancement/custom_link_names
Browse files Browse the repository at this point in the history
Custom link names
  • Loading branch information
martastain authored Jan 12, 2024
2 parents a00d04d + a19fbd2 commit 274ff19
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 49 deletions.
39 changes: 26 additions & 13 deletions api/links/links.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,18 @@ class CreateLinkRequestModel(OPModel):

input: str = Field(..., description="The ID of the input entity.")
output: str = Field(..., description="The ID of the output entity.")
link: str = Field(
...,
description="The name of the link type to create.",
name: str | None = Field(None, description="The name of the link.")
link: str | None = Field(
None,
description="Link type to create. This is deprecated. Use linkType instead.",
example="reference|folder|version",
)
link_type: str | None = Field(
None,
description="Link type to create.",
example="reference|folder|version",
)
data: dict[str, Any] = Field(default_factory=dict, description="Link data")


@router.post("/projects/{project_name}/links")
Expand All @@ -140,15 +147,19 @@ async def create_entity_link(
) -> EntityIdResponse:
"""Create a new entity link."""

link_type, input_type, output_type = post_data.link.split("|")
# TODO: access control. Since we need entity class for that,
# we could get rid of the following check and use Entity.load instead

link_type = post_data.link_type or post_data.link
assert link_type is not None, "Link type is not specified"
assert len(link_type.split("|")) == 3, "Invalid link type format"

link_type_name, input_type, output_type = link_type.split("|")
link_id = EntityID.create()

if input_type == output_type and post_data.input == post_data.output:
raise BadRequestException("Cannot link an entity to itself.")

# TODO: access control. Since we need entity class for that,
# we could get rid of the following check and use Entity.load instead

# Ensure input_id is in the project

query = f"""
Expand Down Expand Up @@ -179,23 +190,25 @@ async def create_entity_link(
await Postgres.execute(
f"""
INSERT INTO project_{project_name}.links
(id, input_id, output_id, link_name, data)
(id, name, input_id, output_id, link_type, author, data)
VALUES
($1, $2, $3, $4, $5)
($1, $2, $3, $4, $5, $6, $7)
""",
link_id,
post_data.name,
post_data.input,
post_data.output,
post_data.link,
{"author": user.name},
link_type,
user.name,
post_data.data,
)
except Postgres.ForeignKeyViolationError:
raise BadRequestException("Unsupported link type.") from None
except Postgres.UniqueViolationError:
raise ConstraintViolationException("Link already exists.") from None

logging.debug(
f"Created {link_type} link between "
f"Created {link_type_name} link between "
f"{input_type} {post_data.input} and "
f"{output_type} {post_data.output}.",
user=user.name,
Expand All @@ -220,7 +233,7 @@ async def delete_entity_link(
"""

query = f"""
SELECT data->'author' as author
SELECT author
FROM project_{project_name}.links
WHERE id = $1
"""
Expand Down
7 changes: 6 additions & 1 deletion ayon_server/graphql/nodes/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class LinkEdge(BaseEdge):
project_name: str = strawberry.field()
entity_type: str = strawberry.field()
entity_id: str = strawberry.field()
name: str | None = strawberry.field(default=None)
link_type: str = strawberry.field(default="something")
direction: str = strawberry.field(default="in")
description: str = strawberry.field(default="")
Expand Down Expand Up @@ -65,7 +66,9 @@ async def links(
self,
info: Info,
direction: str | None = None,
link_types: list[str] = [],
link_types: list[str] | None = None,
names: list[str] | None = None,
name_ex: str | None = None,
first: int = 100,
after: str | None = None,
) -> LinksConnection:
Expand All @@ -75,6 +78,8 @@ async def links(
info=info,
direction=direction,
link_types=link_types,
names=names,
name_ex=name_ex,
first=first,
after=after,
)
32 changes: 21 additions & 11 deletions ayon_server/graphql/resolvers/links.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ async def get_links(
root,
info: Info,
direction: Literal["in", "out"] | None,
link_types: list[str],
first: int,
after: str,
link_types: list[str] | None,
first: int = 100,
after: str | None = None,
names: list[str] | None = None,
name_ex: str | None = None,
) -> LinksConnection:
project_name = root.project_name

Expand All @@ -29,16 +31,23 @@ async def get_links(
sql_conditions.append(f"(input_id = '{root.id}' or output_id = '{root.id}')")

type_conditions = []
for lt in link_types:
type_conditions.append(f"link_name LIKE '{lt}|%'")
if type_conditions:
sql_conditions.append(f"({' or '.join(type_conditions)})")
if link_types is not None:
for lt in link_types:
type_conditions.append(f"link_type LIKE '{lt}|%'")
if type_conditions:
sql_conditions.append(f"({' or '.join(type_conditions)})")

if after is not None and after.isdigit():
sql_conditions.append(f"id > {after}")
sql_conditions.append(f"creation_order > {after}")

if names is not None:
sql_conditions.append(f"name in {SQLTool.array(names)}")

if name_ex is not None:
sql_conditions.append(f"name ~ '{name_ex}'")

query = f"""
SELECT id, input_id, output_id, link_name, data, created_at
SELECT id, name, input_id, output_id, link_type, author, data, created_at
FROM project_{project_name}.links
{SQLTool.conditions(sql_conditions)}
ORDER BY creation_order
Expand All @@ -49,7 +58,7 @@ async def get_links(
if first <= len(edges):
break

link_type, input_type, output_type = row["link_name"].split("|")
link_type, input_type, output_type = row["link_type"].split("|")
input_id = row["input_id"]
output_id = row["output_id"]
link_id = row["id"]
Expand All @@ -74,10 +83,11 @@ async def get_links(
direction=direction,
entity_id=entity_id,
entity_type=entity_type,
name=row["name"],
link_type=link_type,
cursor=link_id,
description=description,
author=row["data"].get("author"),
author=row["author"],
)
)

Expand Down
2 changes: 1 addition & 1 deletion ayon_server/graphql/resolvers/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async def get_versions(
return VersionsConnection()
sql_conditions.append(f"versions.id IN {SQLTool.id_array(ids)}")
if version:
sql_conditions.append(f"version.version = {version}")
sql_conditions.append(f"versions.version = {version}")
if versions is not None:
if not versions:
return VersionsConnection()
Expand Down
8 changes: 4 additions & 4 deletions linker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
from linker.linker import make_links


async def create_link_type(project_name: str, link_type_name: str) -> None:
link_type, input_type, output_type = link_type_name.split("|")
async def create_link_type(project_name: str, link_type: str) -> None:
link_type_name, input_type, output_type = link_type.split("|")
await Postgres.execute(
f"""
INSERT INTO project_{project_name}.link_types
(name, input_type, output_type, link_type)
VALUES
($1, $2, $3, $4)
""",
link_type_name,
link_type,
input_type,
output_type,
link_type,
link_type_name,
)


Expand Down
24 changes: 11 additions & 13 deletions linker/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,29 @@ async def create_link(
project_name: str,
input_id: str,
output_id: str,
link_type_name: str,
link_type: str,
**kwargs,
) -> None:
link_type, input_type, output_type = link_type_name.split("|")
link_type_name, input_type, output_type = link_type.split("|")
link_id = EntityID.create()

if DEBUG:
logging.debug(
f"Creating {link_type} link between "
f"Creating {link_type_name} link between "
f"{input_type} {input_id} and "
f"{output_type} {output_id}"
)
await Postgres.execute(
f"""
INSERT INTO project_{project_name}.links
(id, input_id, output_id, link_name, data)
(id, input_id, output_id, link_type, data)
VALUES
($1, $2, $3, $4, $5)
""",
link_id,
input_id,
output_id,
link_type_name,
link_type,
kwargs,
)

Expand All @@ -45,13 +45,13 @@ async def make_links(
link_type_config: dict[str, Any],
) -> None:
logging.info(f"Creating links in project {project_name}")
link_type_name = link_type_config["link_type"]
link_type, input_type, output_type = link_type_name.split("|")
link_type = link_type_config["link_type"]
link_type_name, input_type, output_type = link_type.split("|")

if "input" not in link_type_config:
raise ValueError(f"Missing input config in link type {link_type_name}")
raise ValueError(f"Missing input config in link type {link_type}")
if "output" not in link_type_config:
raise ValueError(f"Missing output config in link type {link_type_name}")
raise ValueError(f"Missing output config in link type {link_type}")

count = 0
async for input_entity in query_entities(
Expand All @@ -72,10 +72,8 @@ async def make_links(
project_name,
input_entity.id,
output_entity.id,
link_type_name,
link_type,
author="martas",
)
count += 1
logging.goodnews(
f"Created {count} {link_type_name} links for project {project_name}"
)
logging.goodnews(f"Created {count} {link_type} links for project {project_name}")
5 changes: 3 additions & 2 deletions schemas/schema.project.sql
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,11 @@ CREATE UNIQUE INDEX link_type_unique_idx ON link_types(input_type, output_type,

CREATE TABLE links (
id UUID NOT NULL PRIMARY KEY,
name VARCHAR,
link_type VARCHAR NOT NULL REFERENCES link_types(name) ON DELETE CASCADE,
input_id UUID NOT NULL,
output_id UUID NOT NULL,
link_name VARCHAR NOT NULL REFERENCES link_types(name) ON DELETE CASCADE,
author VARCHAR, -- REFERENCES public.users(name) ON UPDATE CASCADE ON DELETE SET NULL,
data JSONB NOT NULL DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ DEFAULT NOW(),
creation_order SERIAL NOT NULL
Expand All @@ -280,7 +282,6 @@ CREATE TABLE links (
CREATE INDEX link_input_idx ON links(input_id);
CREATE INDEX link_output_idx ON links(output_id);
CREATE UNIQUE INDEX link_creation_order_idx ON links(creation_order);
CREATE UNIQUE INDEX link_unique_idx ON links(input_id, output_id, link_name);

--------------
-- SETTINGS --
Expand Down
49 changes: 45 additions & 4 deletions schemas/schema.public.update.sql
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ CREATE OR REPLACE FUNCTION add_thumbnail_id_to_tasks ()
SELECT add_thumbnail_id_to_tasks();
DROP FUNCTION IF EXISTS add_thumbnail_id_to_tasks();

----------------
-- Ayon 1.0.0 --
----------------
-------------------
-- Ayon 1.0.0-RC --
-------------------

-- Copy siteId to instanceId, if instanceId does not exist
-- (this is a one-time migration)
Expand All @@ -169,7 +169,7 @@ DO $$
BEGIN
-- Check if the 'config' table exists
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'config') THEN
-- Execute your insert query

INSERT INTO config (key, value)
SELECT 'instanceId', value
FROM config
Expand All @@ -181,3 +181,44 @@ BEGIN
);
END IF;
END $$;

--------------------
-- Ayon 1.0.0-RC5 --
--------------------

-- refactor links

CREATE OR REPLACE FUNCTION refactor_links() RETURNS VOID AS
$$
DECLARE rec RECORD;
BEGIN
FOR rec IN select distinct nspname from pg_namespace where nspname like 'project_%'
LOOP
IF NOT EXISTS(
SELECT 1 FROM information_schema.columns
WHERE table_schema = rec.nspname
AND table_name = 'links'
AND column_name = 'name'
)
THEN
-- project links table does not have name column, so we need to create it
-- and do some data migration
RAISE WARNING 'Refactoring links in %', rec.nspname;
EXECUTE 'SET LOCAL search_path TO ' || quote_ident(rec.nspname);

ALTER TABLE IF EXISTS links ADD COLUMN name VARCHAR;
ALTER TABLE links RENAME COLUMN link_name TO link_type;
ALTER TABLE links ADD COLUMN author VARCHAR NULL;
UPDATE links SET author = data->>'author';

DROP INDEX link_unique_idx;
END IF;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;

SELECT refactor_links();
DROP FUNCTION IF EXISTS refactor_links();


0 comments on commit 274ff19

Please sign in to comment.