Skip to content

Commit ec23015

Browse files
authored
Merge pull request #52 from eScienceLab/develop
v0.1 Release
2 parents 06d6d75 + 99bcb1b commit ec23015

27 files changed

+1068
-12
lines changed

.dockerignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Git
2+
**/.git
3+
**/.github
4+
**/.gitignore
5+
**/.gitattributes
6+
7+
8+
# Environments
9+
example.env
10+
.env
11+
.venv
12+
env/
13+
venv/
14+
ENV/
15+
env.bak/
16+
venv.bak/
17+
18+
# OSX files
19+
**/.DS_Store
20+
21+
# Docker
22+
Dockerfile
23+
docker-compose*
24+
.dockerignore
25+
26+
# Python
27+
**/__pycache__

.github/workflows/build.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#
2+
name: Create and publish a Docker image
3+
4+
# Configures this workflow to run every time a tagged release is created.
5+
on:
6+
release:
7+
types: [published]
8+
workflow_dispatch:
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: ${{ github.repository }}
13+
14+
jobs:
15+
build-and-push-image:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
packages: write
20+
attestations: write
21+
id-token: write
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
- name: Log in to the Container registry
26+
uses: docker/login-action@v3
27+
with:
28+
registry: ${{ env.REGISTRY }}
29+
username: ${{ github.actor }}
30+
password: ${{ secrets.GITHUB_TOKEN }}
31+
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
32+
- name: Extract metadata (tags, labels) for Docker
33+
id: meta
34+
uses: docker/metadata-action@v5
35+
with:
36+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
37+
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
38+
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see [Usage](https://github.com/docker/build-push-action#usage) in the README of the `docker/build-push-action` repository.
39+
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
40+
- name: Build and push Docker image
41+
id: push
42+
uses: docker/build-push-action@v6
43+
with:
44+
context: .
45+
push: true
46+
tags: ${{ steps.meta.outputs.tags }}
47+
labels: ${{ steps.meta.outputs.labels }}
48+
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see [Using artifact attestations to establish provenance for builds](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds).
49+
- name: Generate artifact attestation
50+
uses: actions/attest-build-provenance@v2
51+
with:
52+
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
53+
subject-digest: ${{ steps.push.outputs.digest }}
54+
push-to-registry: true
55+

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM python:3.11-slim
2+
3+
# Install required system packages, including git
4+
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
5+
6+
WORKDIR /app
7+
8+
COPY requirements.txt .
9+
RUN pip install --upgrade pip
10+
RUN pip install --no-cache-dir -r requirements.txt
11+
12+
COPY cratey.py LICENSE /app/
13+
COPY app /app/app
14+
15+
RUN useradd -ms /bin/bash flaskuser
16+
RUN chown -R flaskuser:flaskuser /app
17+
18+
USER flaskuser
19+
20+
EXPOSE 5000
21+
22+
CMD ["flask", "run", "--host=0.0.0.0"]
23+
24+
LABEL org.opencontainers.image.source="https://github.com/eScienceLab/Cratey-Validator"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 eScience Lab, University of Manchester
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# RO-Crate Validation Service
2+
3+
This project presents a Flask-based API for validating RO-Crates.
4+
5+
## Project Structure
6+
7+
```
8+
app/
9+
├── ro_crates/
10+
│ ├── routes/
11+
│ │ ├── __init__.py # Registers blueprints
12+
│ │ └── post_routes.py # POST API routes
13+
│ └── __init__.py
14+
├── services/
15+
│ ├── logging_service.py # Centralised logging
16+
│ └── validation_service.py # Queue RO-Crates for validation
17+
├── tasks/
18+
│ └── validation_tasks.py # Validate RO-Crates
19+
├── utils/
20+
│ ├── config.py # Configuration
21+
│ ├── minio_utils.py # Methods for interacting with MinIO
22+
│ └── webhook_utils.py # Methods for sending webhooks
23+
```
24+
25+
## Setting up the project
26+
27+
### Prerequisites
28+
29+
- Docker with Docker Compose
30+
31+
### Installation
32+
33+
1. Clone the repository:
34+
```bash
35+
git clone https://github.com/eScienceLab/Cratey-Validator.git
36+
cd crate-validation-service
37+
```
38+
39+
2. Create the `.env` file for shared environment information. An example environment file is included (`example.env`), which can be copied for this purpose. But make sure to change any security settings (username and passwords).
40+
41+
3. Build and start the services using Docker Compose:
42+
```bash
43+
docker compose up --build
44+
```
45+
46+
4. Set up the MinIO bucket
47+
1. Open the MinIO web interface at `http://localhost:9000`.
48+
2. Log in with your MinIO credentials.
49+
3. Create a new bucket named `ro-crates`.
50+
4. **Enable versioning** for the `ro-crates` bucket — this is important for tracking unique object versions.
51+
52+
![Ensure MinIO versioning is enabled](docs/assets/minio-versioning-enabled.webp "Ensure MinIO versioning is enabled")
53+
54+
5. Upload your RO-Crate files to the `ro-crates` bucket.
55+
6. To verify that versioning is enabled:
56+
- Select the uploaded RO-Crate object in the `ro-crates` bucket.
57+
- Navigate to the **Actions** panel on the right.
58+
- The **Display Object Versions** option should be clickable.
59+
60+
![Validate MinIO versioning is enabled](docs/assets/validate-minio-versioning-enabled.webp "Validate MinIO versioning is enabled")
61+
62+
63+
## Development
64+
65+
For standard usage the Docker Compose script uses prebuilt containers.
66+
For testing locally developed containers use the alternate Docker Compose file:
67+
```bash
68+
docker compose --file docker-compose-develop.yml up --build
69+
```
70+
71+
## Example Usage
72+
73+
```
74+
curl -X 'POST' \
75+
'http://localhost:5001/ro_crates/validate_by_id_no_webhook' \
76+
-H 'accept: application/json' \
77+
-H 'Content-Type: application/json' \
78+
-d '{
79+
"crate_id": "1"
80+
}'
81+
```

app.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

app/__init__.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Initialises and configures Flask, integrates Celery, and registers application blueprints."""
2+
3+
# Author: Alexander Hambley
4+
# License: MIT
5+
# Copyright (c) 2025 eScience Lab, The University of Manchester
6+
7+
import os
8+
9+
from apiflask import APIFlask
10+
11+
from app.celery_worker import make_celery, celery
12+
from app.ro_crates.routes import ro_crates_post_bp, ro_crates_get_bp
13+
from app.utils.config import DevelopmentConfig, ProductionConfig, make_celery
14+
15+
16+
def create_app() -> APIFlask:
17+
"""
18+
Creates and configures Flask application.
19+
20+
:return: Flask: A configured Flask application instance.
21+
"""
22+
app = APIFlask(__name__)
23+
app.register_blueprint(ro_crates_post_bp, url_prefix="/ro_crates")
24+
app.register_blueprint(ro_crates_get_bp, url_prefix="/ro_crates")
25+
26+
# Load configuration:
27+
if os.getenv("FLASK_ENV") == "production":
28+
app.config.from_object(ProductionConfig)
29+
else:
30+
# Development environment:
31+
app.debug = True
32+
print("URL Map:")
33+
for rule in app.url_map.iter_rules():
34+
print(rule)
35+
app.config.from_object(DevelopmentConfig)
36+
37+
# Integrate Celery
38+
make_celery(app)
39+
40+
return app

app/celery_worker.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Configures and initialises a Celery instance for use with a Flask application."""
2+
3+
# Author: Alexander Hambley
4+
# License: MIT
5+
# Copyright (c) 2025 eScience Lab, The University of Manchester
6+
7+
from celery import Celery
8+
from flask import Flask
9+
10+
11+
def make_celery(app: Flask = None) -> Celery:
12+
"""
13+
Create and configure a Celery instance using Flask configuration.
14+
15+
:param app: The Flask application. Configuration will be used to initialise Celery.
16+
:return: Celery: A configured Celery instance.
17+
:raises ValueError: If the Flask configuration values are not provided.
18+
"""
19+
if not app:
20+
raise ValueError(
21+
"A Flask application instance must be provided to configure Celery."
22+
)
23+
24+
if "CELERY_RESULT_BACKEND" not in app.config:
25+
raise ValueError(
26+
"Missing configuration: 'CELERY_RESULT_BACKEND' is not defined in the Flask app config."
27+
)
28+
29+
if "CELERY_BROKER_URL" not in app.config:
30+
raise ValueError(
31+
"Missing configuration: 'CELERY_BROKER_URL' is not defined in the Flask app config."
32+
)
33+
34+
celery_instance = Celery(
35+
app.import_name,
36+
backend=app.config["CELERY_RESULT_BACKEND"],
37+
broker=app.config["CELERY_BROKER_URL"],
38+
)
39+
40+
if app:
41+
celery_instance.conf.update(app.config)
42+
43+
return celery_instance
44+
45+
46+
celery = Celery()

app/ro_crates/__init__.py

Whitespace-only changes.

app/ro_crates/routes/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Defines main Blueprint and registers sub-Blueprints for organising related routes."""
2+
3+
# Author: Alexander Hambley
4+
# License: MIT
5+
# Copyright (c) 2025 eScience Lab, The University of Manchester
6+
7+
from apiflask import APIBlueprint
8+
9+
from app.ro_crates.routes.post_routes import post_routes_bp
10+
from app.ro_crates.routes.get_routes import get_routes_bp
11+
12+
#ro_crates_bp = APIBlueprint("ro_crates", __name__)
13+
14+
#ro_crates_bp.register_blueprint(post_routes_bp, url_prefix="/post")
15+
16+
ro_crates_post_bp = post_routes_bp
17+
ro_crates_get_bp = get_routes_bp

0 commit comments

Comments
 (0)