Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54cd1e585a |
@@ -1,43 +0,0 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||
{
|
||||
"name": "Python 3",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
|
||||
},
|
||||
|
||||
"remoteEnv": {
|
||||
"SPECKLE_TOKEN": "foobar"
|
||||
},
|
||||
"containerEnv": {
|
||||
"SPECKLE_TOKEN": "asdfasdf"
|
||||
},
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "cp .env.example .env && POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-python.python",
|
||||
"ms-python.black-formatter",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"mikestead.dotenv"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
SPECKLE_TOKEN=mytoken
|
||||
+18
-15
@@ -1,17 +1,21 @@
|
||||
name: 'build and deploy Speckle functions'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
on: # rebuild any PRs and any branch changes
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
publish-automate-function-version: # make sure the action works on a clean machine without building
|
||||
env:
|
||||
FUNCTION_SCHEMA_FILE_NAME: functionSchema.json
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.4.0
|
||||
- uses: actions/checkout@v3.4.0
|
||||
with:
|
||||
repository: 'specklesystems/speckle-automate-github-composite-action'
|
||||
path: 'github-action'
|
||||
ref: main
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
@@ -25,14 +29,13 @@ jobs:
|
||||
- name: Restore dependencies
|
||||
run: poetry install --no-root
|
||||
- name: Extract functionInputSchema
|
||||
id: extract_schema
|
||||
run: |
|
||||
python main.py generate_schema ${HOME}/${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
||||
- name: Speckle Automate Function - Build and Publish
|
||||
uses: specklesystems/speckle-automate-github-composite-action@0.6.7
|
||||
echo "function_input_schema=$(python schema_generation.py)" >> "$GITHUB_ENV"
|
||||
- uses: ./github-action
|
||||
id: function_publish
|
||||
with:
|
||||
speckle_automate_url: ${{ env.SPECKLE_AUTOMATE_URL || 'https://automate.speckle.dev' }}
|
||||
speckle_token: ${{ secrets.SPECKLE_FUNCTION_TOKEN }}
|
||||
speckle_function_id: ${{ secrets.SPECKLE_FUNCTION_ID }}
|
||||
speckle_function_input_schema_file_path: ${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
||||
speckle_function_command: 'python main.py run'
|
||||
speckle_server_url: 'https://automate.speckle.dev'
|
||||
speckle_token: ${{ secrets.SPECKLE_AUTOMATE_FUNCTION_PUBLISH_TOKEN }}
|
||||
speckle_function_id: ${{ secrets.SPECKLE_AUTOMATE_FUNCTION_ID }}
|
||||
speckle_function_input_schema: ${{ env.function_input_schema }}
|
||||
speckle_function_command: 'python main.py'
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,python,pycharm
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,python,pycharm
|
||||
|
||||
**/.env
|
||||
**/.envrc
|
||||
|
||||
**/.tool-versions
|
||||
|
||||
### PyCharm ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
python 3.11.0
|
||||
Vendored
+4
-11
@@ -5,19 +5,12 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Speckle Automate function",
|
||||
"name": "Python: Current File",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "main.py",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"args": [
|
||||
"run",
|
||||
"{\"projectId\": \"843d07eb10\", \"modelId\": \"base design\", \"versionId\": \"2a32ccfee1\", \"speckleServerUrl\": \"https://latest.speckle.systems\"}",
|
||||
// make sure to use camelCase for variable names
|
||||
"{\"forbiddenSpeckleType\": \"Objects.Geometry.Brep\"}"
|
||||
]
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+1
-7
@@ -1,9 +1,3 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"camelcase",
|
||||
"pydantic",
|
||||
"stringcase",
|
||||
"typer"
|
||||
],
|
||||
"python.defaultInterpreterPath": ".venv/bin/python"
|
||||
"cSpell.words": ["camelcase", "pydantic", "stringcase", "typer"]
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ RUN pip install poetry
|
||||
|
||||
COPY . .
|
||||
RUN poetry export -f requirements.txt --output requirements.txt && pip install -r requirements.txt
|
||||
# RUN poetry install --no-root --no-dev
|
||||
|
||||
@@ -1,73 +1 @@
|
||||
# Speckle Automate function template - Python
|
||||
|
||||
|
||||
This is a template repository for a Speckle Automate functions written in python
|
||||
using the [specklepy](https://pypi.org/project/specklepy/) SDK to interact with Speckle data.
|
||||
|
||||
This template contains the full scaffolding required to publish a function to the automate environment.
|
||||
Also has some sane defaults for a development environment setups.
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Use this template repository to create a new repository in your own / organization's profile.
|
||||
|
||||
Register the function
|
||||
|
||||
### Add new dependencies
|
||||
|
||||
To add new python package dependencies to the project, use:
|
||||
`$ poetry add pandas`
|
||||
|
||||
### Change launch variables
|
||||
|
||||
describe how the launch.json should be edited
|
||||
|
||||
### Github Codespaces
|
||||
|
||||
create new repo from template, and use the create new code
|
||||
|
||||
|
||||
### Local dev environment
|
||||
|
||||
|
||||
|
||||
|
||||
# Archive
|
||||
|
||||
This is a simple example of how to use the Speckle Automate Python package to automate the creation of a Speckle stream.
|
||||
|
||||
|
||||
## Using this Speckle Function
|
||||
|
||||
1. [Create](https://automate.speckle.dev/) a new Speckle Automation.
|
||||
1. Select your Speckle Project and Speckle Model.
|
||||
1. Select the existing Speckle Function named [`Random comment on IFC beam`](https://automate.speckle.dev/functions/e110be8fad).
|
||||
1. Enter a phrase to use in the comment.
|
||||
1. Click `Create Automation`.
|
||||
|
||||
## Getting Started with creating your own Speckle Function
|
||||
|
||||
1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) this repository.
|
||||
1. [Clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo#cloning-your-forked-repository) your forked repository to your development environment, or use [GitHub CodeSpaces](https://github.com/features/codespaces).
|
||||
1. [Register](https://automate.speckle.dev/) your Function with [Speckle Automate](https://automate.speckle.dev/).
|
||||
1. After completing the registration of the Function you will be shown a Function Publish Token and a Function ID. You will need these later.
|
||||
1. Save your Function Publish Token as a [GitHub Action Secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `SPECKLE_AUTOMATE_FUNCTION_PUBLISH_TOKEN`.
|
||||
1. Save your Function ID as a [GitHub Action Secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `SPECKLE_AUTOMATE_FUNCTION_ID`.
|
||||
1. Make changes to your Function in `main.py`. See below for the Developer Requirements, and instructions on how to test.
|
||||
1. Every commit to `main` branch will create a new version of your Speckle Function.
|
||||
|
||||
## Developer Requirements
|
||||
|
||||
1. Install the following:
|
||||
- [Python 3](https://www.python.org/downloads/)
|
||||
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)
|
||||
1. Run `poetry shell && poetry install` to install the required Python packages.
|
||||
|
||||
## Building and Testing
|
||||
|
||||
The code can be tested locally by running `poetry run pytest`.
|
||||
The code should also be packaged into the format required by Speckle Automate, a Docker Container Image, and that should also be tested.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Learn](https://speckle.guide/dev/python.html) more about SpecklePy, and interacting with Speckle from Python.
|
||||
# Go Automate Go
|
||||
+3
-7
@@ -1,13 +1,9 @@
|
||||
"""Helper module for a simple speckle object tree flattening."""
|
||||
|
||||
from collections.abc import Iterable
|
||||
|
||||
from typing import Iterable
|
||||
from specklepy.objects import Base
|
||||
|
||||
|
||||
def flatten_base(base: Base) -> Iterable[Base]:
|
||||
"""Take a base and flatten it to an iterable of bases."""
|
||||
if hasattr(base, "elements"):
|
||||
for element in base["elements"]:
|
||||
for element in base.elements:
|
||||
yield from flatten_base(element)
|
||||
yield base
|
||||
yield base
|
||||
@@ -1,94 +1,75 @@
|
||||
"""This module contains the business logic of the function.
|
||||
|
||||
use the automation_context module to wrap your function in an Autamate context helper
|
||||
"""
|
||||
|
||||
from pydantic import Field
|
||||
from speckle_automate import (
|
||||
AutomateBase,
|
||||
AutomationContext,
|
||||
execute_automate_function,
|
||||
)
|
||||
import typer
|
||||
from pydantic import BaseModel
|
||||
from stringcase import camelcase
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.transports.server import ServerTransport
|
||||
from specklepy.api.operations import receive
|
||||
from specklepy.api.client import SpeckleClient
|
||||
import random
|
||||
|
||||
from flatten import flatten_base
|
||||
from make_comment import make_comment
|
||||
|
||||
|
||||
class FunctionInputs(AutomateBase):
|
||||
"""These are function author defined values.
|
||||
class SpeckleProjectData(BaseModel):
|
||||
"""Values of the project / model that triggered the run of this function."""
|
||||
|
||||
Automate will make sure to supply them matching the types specified here.
|
||||
Please use the pydantic model schema to define your inputs:
|
||||
https://docs.pydantic.dev/latest/usage/models/
|
||||
project_id: str
|
||||
model_id: str
|
||||
version_id: str
|
||||
speckle_server_url: str
|
||||
|
||||
class Config:
|
||||
alias_generator = camelcase
|
||||
|
||||
|
||||
class FunctionInputs(BaseModel):
|
||||
"""
|
||||
These are function author defined values, automate will make sure to supply them.
|
||||
"""
|
||||
|
||||
forbidden_speckle_type: str = Field(
|
||||
title="Forbidden speckle type",
|
||||
description=(
|
||||
"If a object has the following speckle_type,"
|
||||
" it will be marked with an error."
|
||||
),
|
||||
comment_text: str
|
||||
|
||||
class Config:
|
||||
alias_generator = camelcase
|
||||
|
||||
|
||||
def main(speckle_project_data: str, function_inputs: str, speckle_token: str):
|
||||
project_data = SpeckleProjectData.parse_raw(speckle_project_data)
|
||||
inputs = FunctionInputs.parse_raw(function_inputs)
|
||||
|
||||
client = SpeckleClient(project_data.speckle_server_url, use_ssl=False)
|
||||
client.authenticate_with_token(speckle_token)
|
||||
commit = client.commit.get(project_data.project_id, project_data.version_id)
|
||||
branch = client.branch.get(project_data.project_id, project_data.model_id, 1)
|
||||
|
||||
memory_transport = MemoryTransport()
|
||||
server_transport = ServerTransport(project_data.project_id, client)
|
||||
base = receive(commit.referencedObject, server_transport, memory_transport)
|
||||
|
||||
random_beam = random.choice(
|
||||
[b for b in flatten_base(base) if b.speckle_type == "IFCBEAM"]
|
||||
)
|
||||
|
||||
make_comment(
|
||||
client,
|
||||
project_data.project_id,
|
||||
branch.id,
|
||||
project_data.version_id,
|
||||
inputs.comment_text,
|
||||
random_beam.id,
|
||||
)
|
||||
|
||||
print(
|
||||
"Ran function with",
|
||||
f"{speckle_project_data} {function_inputs}",
|
||||
)
|
||||
|
||||
|
||||
def automate_function(
|
||||
automate_context: AutomationContext,
|
||||
function_inputs: FunctionInputs,
|
||||
) -> None:
|
||||
"""This is an example Speckle Automate function.
|
||||
|
||||
Args:
|
||||
automate_context: A context helper object, that carries relevant information
|
||||
about the runtime context of this function.
|
||||
It gives access to the Speckle project data, that triggered this run.
|
||||
It also has conveniece methods attach result data to the Speckle model.
|
||||
function_inputs: An instance object matching the defined schema.
|
||||
"""
|
||||
# the context provides a conveniet way, to receive the triggering version
|
||||
version_root_object = automate_context.receive_version()
|
||||
|
||||
count = 0
|
||||
for b in flatten_base(version_root_object):
|
||||
if b.speckle_type == function_inputs.forbidden_speckle_type:
|
||||
if not b.id:
|
||||
raise ValueError("Cannot operate on objects without their id's.")
|
||||
automate_context.add_object_error(
|
||||
b.id,
|
||||
"This project should not contain the type: " f"{b.speckle_type}",
|
||||
)
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
# this is how a run is marked with a failure cause
|
||||
automate_context.mark_run_failed(
|
||||
"Automation failed: "
|
||||
f"Found {count} object that have one of the forbidden speckle types: "
|
||||
f"{function_inputs.forbidden_speckle_type}"
|
||||
)
|
||||
|
||||
else:
|
||||
automate_context.mark_run_success("No forbidden types found.")
|
||||
|
||||
# if the function generates file results, this is how it can be
|
||||
# attached to the Speckle project / model
|
||||
# automate_context.store_file_result("./report.pdf")
|
||||
|
||||
|
||||
def automate_function_without_inputs(automate_context: AutomationContext) -> None:
|
||||
"""A function example without inputs.
|
||||
|
||||
If your function does not need any input variables,
|
||||
besides what the automation context provides,
|
||||
the inputs argument can be omitted.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# make sure to call the function with the executor
|
||||
if __name__ == "__main__":
|
||||
# NOTE: always pass in the automate function by its reference, do not invoke it!
|
||||
|
||||
# pass in the function reference with the inputs schema to the executor
|
||||
execute_automate_function(automate_function, FunctionInputs)
|
||||
|
||||
# if the function has no arguments, the executor can handle it like so
|
||||
# execute_automate_function(automate_function_without_inputs)
|
||||
# main(
|
||||
# '{"projectId":"bbb3aba8d4", "modelId":"automateTest", "versionId": "d37ee808db", "speckleServerUrl": "http://hyperion:3000" }',
|
||||
# '{"commentText": "automate made me to do this"}',
|
||||
# "c3e6536e570a94e5d84590c51b29198b26dce89439",
|
||||
# )
|
||||
typer.run(main)
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from gql import gql
|
||||
|
||||
|
||||
def make_comment(
|
||||
client: SpeckleClient,
|
||||
project_id: str,
|
||||
model_id: str,
|
||||
version_id: str,
|
||||
comment_text: str,
|
||||
selected_object_id: str,
|
||||
) -> None:
|
||||
client.httpclient.execute(
|
||||
gql(
|
||||
"""
|
||||
mutation createComment($input: CreateCommentInput!) {
|
||||
commentMutations {
|
||||
create(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
),
|
||||
{
|
||||
"input": {
|
||||
"content": {
|
||||
"blobIds": [],
|
||||
"doc": {
|
||||
"content": [
|
||||
{
|
||||
"content": [{"text": comment_text, "type": "text"}],
|
||||
"type": "paragraph",
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
},
|
||||
},
|
||||
"projectId": project_id,
|
||||
"resourceIdString": model_id,
|
||||
"screenshot": None,
|
||||
"viewerState": {
|
||||
"projectId": project_id,
|
||||
"resources": {
|
||||
"request": {
|
||||
"resourceIdString": f"{model_id}@{version_id}",
|
||||
"threadFilters": {},
|
||||
}
|
||||
},
|
||||
"sessionId": "fooobarbaz",
|
||||
"ui": {
|
||||
"camera": {
|
||||
"isOrthoProjection": False,
|
||||
"position": [
|
||||
-13.959975903859306,
|
||||
109.21340462426888,
|
||||
19.00868018548827,
|
||||
],
|
||||
"target": [
|
||||
-28.304303646087646,
|
||||
99.69336318969727,
|
||||
2.3997000455856323,
|
||||
],
|
||||
"zoom": 1,
|
||||
},
|
||||
"explodeFactor": 0,
|
||||
"filters": {
|
||||
"hiddenObjectIds": [],
|
||||
"isolatedObjectIds": [selected_object_id],
|
||||
"propertyFilter": {"isApplied": False, "key": None},
|
||||
"selectedObjectIds": [selected_object_id],
|
||||
},
|
||||
"lightConfig": {
|
||||
"azimuth": 0.75,
|
||||
"castShadow": True,
|
||||
"color": 16777215,
|
||||
"elevation": 1.33,
|
||||
"enabled": True,
|
||||
"indirectLightIntensity": 1.2,
|
||||
"intensity": 5,
|
||||
"radius": 0,
|
||||
"shadowcatcher": True,
|
||||
},
|
||||
"sectionBox": None,
|
||||
"selection": [
|
||||
-31.355755138199026,
|
||||
101.06821903317298,
|
||||
4.250507316347136,
|
||||
],
|
||||
"spotlightUserSessionId": None,
|
||||
"threads": {
|
||||
"openThread": {
|
||||
"isTyping": False,
|
||||
"newThreadEditor": True,
|
||||
"threadId": None,
|
||||
}
|
||||
},
|
||||
},
|
||||
"viewer": {"metadata": {"filteringState": {}}},
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
Generated
+334
-510
File diff suppressed because it is too large
Load Diff
+5
-15
@@ -7,27 +7,17 @@ readme = "README.md"
|
||||
packages = [{include = "src/speckle_automate_py"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
specklepy = "^2.17.0"
|
||||
python = "^3.10"
|
||||
specklepy = "^2.14.1"
|
||||
typer = "^0.9.0"
|
||||
pydantic = "^1.10.8"
|
||||
stringcase = "^1.2.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^23.3.0"
|
||||
mypy = "^1.3.0"
|
||||
ruff = "^0.0.271"
|
||||
pytest = "^7.4.2"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"UP", # pyupgrade
|
||||
"D", # pydocstyle
|
||||
"I", # isort
|
||||
]
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
import json
|
||||
from main import FunctionInputs
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(json.dumps(FunctionInputs.schema()))
|
||||
@@ -1,158 +0,0 @@
|
||||
"""Run integration tests with a speckle server."""
|
||||
import os
|
||||
import secrets
|
||||
import string
|
||||
|
||||
import pytest
|
||||
from gql import gql
|
||||
from speckle_automate import (
|
||||
AutomationRunData,
|
||||
AutomationStatus,
|
||||
run_function,
|
||||
)
|
||||
from specklepy.api import operations
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.transports.server import ServerTransport
|
||||
|
||||
from main import FunctionInputs, automate_function
|
||||
|
||||
|
||||
def crypto_random_string(length: int) -> str:
|
||||
"""Generate a semi crypto random string of a given length."""
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
return "".join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
|
||||
def register_new_automation(
|
||||
project_id: str,
|
||||
model_id: str,
|
||||
speckle_client: SpeckleClient,
|
||||
automation_id: str,
|
||||
automation_name: str,
|
||||
automation_revision_id: str,
|
||||
):
|
||||
"""Register a new automation in the speckle server."""
|
||||
query = gql(
|
||||
"""
|
||||
mutation CreateAutomation(
|
||||
$projectId: String!
|
||||
$modelId: String!
|
||||
$automationName: String!
|
||||
$automationId: String!
|
||||
$automationRevisionId: String!
|
||||
) {
|
||||
automationMutations {
|
||||
create(
|
||||
input: {
|
||||
projectId: $projectId
|
||||
modelId: $modelId
|
||||
automationName: $automationName
|
||||
automationId: $automationId
|
||||
automationRevisionId: $automationRevisionId
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"projectId": project_id,
|
||||
"modelId": model_id,
|
||||
"automationName": automation_name,
|
||||
"automationId": automation_id,
|
||||
"automationRevisionId": automation_revision_id,
|
||||
}
|
||||
speckle_client.httpclient.execute(query, params)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def speckle_token() -> str:
|
||||
"""Provide a speckle token for the test suite."""
|
||||
env_var = "SPECKLE_TOKEN"
|
||||
token = os.getenv(env_var)
|
||||
if not token:
|
||||
raise ValueError(f"Cannot run tests without a {env_var} environment variable")
|
||||
return token
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def speckle_server_url() -> str:
|
||||
"""Provide a speckle server url for the test suite, default to localhost."""
|
||||
return os.getenv("SPECKLE_SERVER_URL", "http://127.0.0.1:3000")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def test_client(speckle_server_url: str, speckle_token: str) -> SpeckleClient:
|
||||
"""Initialize a SpeckleClient for testing."""
|
||||
test_client = SpeckleClient(
|
||||
speckle_server_url, speckle_server_url.startswith("https")
|
||||
)
|
||||
test_client.authenticate_with_token(speckle_token)
|
||||
return test_client
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def test_object() -> Base:
|
||||
"""Create a Base model for testing."""
|
||||
root_object = Base()
|
||||
root_object.foo = "bar"
|
||||
return root_object
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def automation_run_data(
|
||||
test_object: Base, test_client: SpeckleClient, speckle_server_url: str
|
||||
) -> AutomationRunData:
|
||||
"""Set up an automation context for testing."""
|
||||
project_id = test_client.stream.create("Automate function e2e test")
|
||||
branch_name = "main"
|
||||
|
||||
model = test_client.branch.get(project_id, branch_name, commits_limit=1)
|
||||
model_id: str = model.id
|
||||
|
||||
root_obj_id = operations.send(
|
||||
test_object, [ServerTransport(project_id, test_client)]
|
||||
)
|
||||
version_id = test_client.commit.create(project_id, root_obj_id)
|
||||
|
||||
automation_name = crypto_random_string(10)
|
||||
automation_id = crypto_random_string(10)
|
||||
automation_revision_id = crypto_random_string(10)
|
||||
|
||||
register_new_automation(
|
||||
project_id,
|
||||
model_id,
|
||||
test_client,
|
||||
automation_id,
|
||||
automation_name,
|
||||
automation_revision_id,
|
||||
)
|
||||
|
||||
automation_run_id = crypto_random_string(10)
|
||||
function_id = crypto_random_string(10)
|
||||
function_revision = crypto_random_string(10)
|
||||
return AutomationRunData(
|
||||
project_id=project_id,
|
||||
model_id=model_id,
|
||||
branch_name=branch_name,
|
||||
version_id=version_id,
|
||||
speckle_server_url=speckle_server_url,
|
||||
automation_id=automation_id,
|
||||
automation_revision_id=automation_revision_id,
|
||||
automation_run_id=automation_run_id,
|
||||
function_id=function_id,
|
||||
function_revision=function_revision,
|
||||
)
|
||||
|
||||
|
||||
def test_function_run(automation_run_data: AutomationRunData, speckle_token: str):
|
||||
"""Run an integration test for the automate function."""
|
||||
automate_sdk = run_function(
|
||||
automate_function,
|
||||
automation_run_data,
|
||||
speckle_token,
|
||||
FunctionInputs(forbidden_speckle_type="Base"),
|
||||
)
|
||||
|
||||
assert automate_sdk.run_status == AutomationStatus.FAILED
|
||||
Reference in New Issue
Block a user