Zum Inhalt

REST API

Die Librario REST API ermöglicht Ihnen die programmatische Integration und Automatisierung Ihrer Bibliotheksprozesse. Sie können damit alle wichtigen Funktionen von Librario in Ihre bestehende IT-Infrastruktur bzw. in Ihre Prozesse einbinden.

Kein Web Scraping

Bitte verwenden Sie für automatisierte Zugriffe ausschließlich die REST API. Das Scraping der Weboberfläche wird nicht unterstützt und kann zu unerwarteten Fehlern führen. Die API bietet alle notwendigen Endpunkte für eine programmatische Integration.

Zugriff auf die API

Die API ist unter folgender URL erreichbar:

https://<ihre-firma>.mylibrar.io/api, zum Beispiel: https://app.mylibrar.io/api

Falls Sie eine eigene Subdomain zugewiesen bekommen haben, können Sie diese in der URL verwenden.

API-Dokumentation

Screenshot der Swagger UI Dokumentation der Librario API.
  Links sind die verschiedenen API-Endpunkte nach Ressourcen gruppiert zu sehen.
  Rechts werden die Details des ausgewählten Endpunkts mit Parametern und Beispielantworten angezeigt.

Die interaktive API-Dokumentation mittels Swagger UI

Die vollständige API-Dokumentation steht Ihnen in zwei Formaten zur Verfügung:

  1. Swagger UI: Eine interaktive Web-Oberfläche zum Erkunden der API

    • Erreichbar unter https://<ihre-firma>.mylibrar.io/api
    • Ermöglicht das direkte Testen von API-Aufrufen
    • Zeigt Beispielanfragen und -antworten
    • Bietet Download der OpenAPI-Spezifikation als swagger.yaml
  2. OpenAPI-Spezifikation (swagger.yaml): Eine maschinenlesbare Beschreibung der API

    • Basis für die automatische Generierung von API-Clients
    • Kann in API-Entwicklungswerkzeuge importiert werden
    • Hilfreich für die Verwendung mit KI-Tools wie ChatGPT oder Claude

Authentifizierung

Die API verwendet Basic Authentication für den Zugriff. Alle Personen können sich mit ihren normalen Librario-Zugangsdaten an der API anmelden. Die Berechtigungen entsprechen dabei den in der Anwendung konfigurierten Rollen.

Technische Benutzer für Integrationen

Es wird empfohlen, für automatisierte Prozesse dedizierte technische Zugänge anzulegen. Dies macht die Integration unabhängig von personenbezogenen Accounts und vereinfacht die Verwaltung von Zugriffsrechten.

Datenformat

Die API nutzt das JSON-Format für den Datenaustausch. Dieses Format ist in allen gängigen Programmiersprachen einfach zu verarbeiten.

Paginierung

Bei Listen-Endpoints wird die Ausgabe auf 25 Einträge pro Seite begrenzt. Um alle Datensätze zu erhalten, müssen Sie die Seiten nacheinander abrufen.

Die Navigation wird durch spezielle Response-Header erleichtert:

  • X-Total: Gesamtanzahl der verfügbaren Einträge
  • Link: URLs für erste/vorherige/nächste/letzte Seite

Der Link Header macht die Implementierung besonders einfach: Solange ein next Link vorhanden ist, gibt es weitere Seiten abzurufen.

Typische Anwendungsfälle

Die folgenden Beispiele zeigen die API-Nutzung mit zwei verschiedenen Ansätzen:

  • curl als einfaches Kommandozeilen-Tool für schnelle Tests und Debugging
  • Ruby mit der eingebauten Net::HTTP Bibliothek für Produktiv-Implementierungen

Benutzer:innen-Management

In vielen Organisationen soll der Lebenszyklus von Benutzerkonten automatisiert werden, etwa wenn neue Mitarbeitende eingestellt werden oder das Unternehmen verlassen.

# Neue:n Benutzer:in anlegen
curl -X POST "https://ihre-firma.mylibrar.io/api/v2/users" \
  -u "tech_user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "max.mustermann@example.com",
    "name": "Max Mustermann",
    "roles": ["librarian"]
  }'

# Rollen zuweisen
curl -X PATCH "https://ihre-firma.mylibrar.io/api/v2/users/123/roles" \
  -u "tech_user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{"roles": ["admin", "librarian"]}'

# Benutzer:in löschen
curl -X DELETE "https://ihre-firma.mylibrar.io/api/v2/users/123" \
  -u "tech_user@example.com:password"
user-management.rb
require 'net/http'
require 'json'
require 'uri'

class LibrarioClient
  def initialize(host:, username:, password:)
    @host = host
    @username = username
    @password = password
  end

  def create_user(email:, name:, roles: [])
    # Neue:n Benutzer:in anlegen
    post('/users', {
      email: email,
      name: name,
      roles: roles
    })
  end

  def assign_roles(user_id:, roles:)
    # Rollen zuweisen/aktualisieren
    patch("/users/#{user_id}/roles", {
      roles: roles
    })
  end

  def delete_user(user_id:)
    # Benutzer:in löschen bei Austritt
    delete("/users/#{user_id}")
  end

  private

  def request(method, path, data = nil)
    uri = URI("https://#{@host}/api/v2#{path}")

    # Request vorbereiten
    request = case method
    when :get    then Net::HTTP::Get.new(uri)
    when :post   then Net::HTTP::Post.new(uri)
    when :patch  then Net::HTTP::Patch.new(uri)
    when :delete then Net::HTTP::Delete.new(uri)
    end

    # Auth & Headers setzen
    request.basic_auth(@username, @password)
    request['Content-Type'] = 'application/json'
    request.body = data.to_json if data

    # Request ausführen
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body) unless response.body.empty?
  end

  def post(path, data)   ; request(:post, path, data)   ; end
  def patch(path, data)  ; request(:patch, path, data)  ; end
  def delete(path)       ; request(:delete, path)       ; end
end

# Verwendung:
client = LibrarioClient.new(
  host: 'ihre-firma.mylibrar.io',
  username: 'tech_user@example.com',
  password: 'password'
)

client.create_user(
  email: "max.mustermann@example.com",
  name: "Max Mustermann",
  roles: ["librarian"]
)

Datenimport

Beim Umstieg auf Librario müssen oft Daten aus dem Altsystem migriert werden. Die API ermöglicht einen strukturierten Import von Publikationen mit zugehörigen Dateien.

Einfache Publikation erstellen

Der einfachste Fall ist das Erstellen einer Publikation ohne angehängte Dateien:

curl -X POST "https://ihre-firma.mylibrar.io/api/v2/publications" \
  -u "tech_user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Beispiel-Publikation",
    "authors": [
      {"name": "Max Mustermann", "type": "person"}
    ],
    "publishable_type": "Book",
    "isbn": "978-3-86680-192-9"
  }'
datenimport.rb
# frozen_string_literal: true

require 'net/http'
require 'json'
require 'uri'

# Einfacher Import einer Publikation ohne Dateien
uri = URI('https://ihre-firma.mylibrar.io/api/v2/publications')

request = Net::HTTP::Post.new(uri)
request.basic_auth('tech_user@example.com', 'password')
request['Content-Type'] = 'application/json'
request.body = {
  title: 'Beispiel-Publikation',
  authors: [
    { name: 'Max Mustermann', type: 'person' }
  ],
  publishable_type: 'Book',
  isbn: '978-3-86680-192-9'
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

if response.is_a?(Net::HTTPSuccess)
  publication = JSON.parse(response.body)
  puts "Publikation erstellt mit ID: #{publication['id']}"
else
  puts "Fehler: #{response.body}"
end

Import mit Dateien

Für den Import von Publikationen mit angehängten Dateien (z.B. PDFs) ist ein mehrstufiger Prozess erforderlich:

Workflow-Übersicht
sequenceDiagram
    participant Client
    participant API
    participant S3

    Client->>API: 1. POST /publications<br/>(Publikation erstellen)
    API-->>Client: publication_id

    Client->>API: 2. GET /assets/presign<br/>(Presigned URL anfordern)
    API-->>Client: presign_response mit<br/>URL & Formularfeldern

    Client->>S3: 3. POST Multipart-Upload<br/>(Datei hochladen)
    S3-->>Client: Success (204)

    Note over Client: File ID extrahieren:<br/>Alles nach "cache/"

    Client->>API: 4. POST /assets<br/>(Asset mit file.id erstellen)
    API-->>Client: asset_id

Wichtiger Hinweis zur File ID

Die file.id für die Asset-Erstellung entspricht dem S3-Pfad ohne das cache/ Präfix.

Beispiel:

S3 Key: cache/1/a078705e6e55b512bb2b596c12a2a12b/dokument.pdf
File ID: 1/a078705e6e55b512bb2b596c12a2a12b/dokument.pdf

Das Backend sucht die Datei unter cache/{file.id} in S3. Bei falscher ID erhält man den Fehler:

file "..." not found on storage (Aws::S3::Errors::NoSuchKey)

#!/bin/bash

# Publikation erstellen
PUBLICATION=$(curl -X POST "https://ihre-firma.mylibrar.io/api/v2/publications" \
  -u "tech_user@example.com:password" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Technische Dokumentation",
    "publishable_type": "Report",
    "published_on": "2024-01-15"
  }')

PUBLICATION_ID=$(echo "$PUBLICATION" | grep -o '"id":[0-9]*' | cut -d: -f2)

# Presigned URL für Upload anfordern
PRESIGN=$(curl -X GET "https://ihre-firma.mylibrar.io/api/v2/assets/presign?filename=dokument.pdf&type=application/pdf" \
  -u "tech_user@example.com:password")

# Wichtige Felder extrahieren
UPLOAD_URL=$(echo "$PRESIGN" | jq -r '.url')
KEY=$(echo "$PRESIGN" | jq -r '.fields.key')
FILE_ID=${KEY#cache/}  # Entfernt "cache/" Präfix

# Content-Disposition aus Policy extrahieren (für S3-Validierung)
POLICY=$(echo "$PRESIGN" | jq -r '.fields.policy')
CD_VALUE=$(echo "$POLICY" | base64 -d | jq -r '.conditions[] | select(has("Content-Disposition")) | .["Content-Disposition"]')

# Datei zu S3 hochladen (--form-string für korrekte Escape-Behandlung)
curl -X POST "$UPLOAD_URL" \
  --form-string "key=$KEY" \
  --form-string "Content-Disposition=$CD_VALUE" \
  --form-string "Content-Type=application/pdf" \
  --form-string "policy=$POLICY" \
  --form-string "x-amz-credential=$(echo "$PRESIGN" | jq -r '.fields["x-amz-credential"]')" \
  --form-string "x-amz-algorithm=$(echo "$PRESIGN" | jq -r '.fields["x-amz-algorithm"]')" \
  --form-string "x-amz-date=$(echo "$PRESIGN" | jq -r '.fields["x-amz-date"]')" \
  --form-string "x-amz-signature=$(echo "$PRESIGN" | jq -r '.fields["x-amz-signature"]')" \
  --form "file=@dokument.pdf"

# Asset erstellen und mit Publikation verknüpfen
curl -X POST "https://ihre-firma.mylibrar.io/api/v2/assets" \
  -u "tech_user@example.com:password" \
  -H "Content-Type: application/json" \
  -d "{
    \"publication_id\": $PUBLICATION_ID,
    \"name\": \"Technische Dokumentation\",
    \"content_type\": \"application/pdf\",
    \"file\": {
      \"id\": \"$FILE_ID\",
      \"storage\": \"cache\",
      \"metadata\": {
        \"size\": $(stat -f%z dokument.pdf),
        \"filename\": \"dokument.pdf\",
        \"mime_type\": \"application/pdf\"
      }
    }
  }"
datenimport-mit-upload.rb
# frozen_string_literal: true

require 'net/http'
require 'json'
require 'uri'
require 'base64'
require 'mime/types'

# LibrarioImport - Klasse für den automatisierten Import von Publikationen und Dateien
#
# Diese Klasse ermöglicht den programmatischen Import von Publikationen
# in Librario über die REST API. Sie unterstützt:
# - Erstellung von Publikationen mit Metadaten
# - Upload von Dateien verschiedener Formate (PDF, Word, Excel, etc.)
# - Automatische MIME-Type Erkennung
# - Fehlerbehandlung und Logging
#
# @example Basis-Verwendung
#   importer = LibrarioImport.new(
#     host: 'ihre-firma.mylibrar.io',
#     username: 'tech_user@example.com',
#     password: 'ihr_passwort'
#   )
#
#   publication = importer.import_publication({
#     title: 'Meine Publikation',
#     publishable_type: 'Book'
#   })
#
# @example Import mit Dateianhang
#   if publication
#     importer.attach_file(
#       publication_id: publication['id'],
#       file_path: '/pfad/zur/datei.pdf',
#       name: 'Volltext'
#     )
#   end
#
class LibrarioImport
  # Initialisiert eine neue Instanz des LibrarioImport
  #
  # @param host [String] Ihre Librario-Domain (z.B. 'ihre-firma.mylibrar.io')
  # @param username [String] E-Mail-Adresse des technischen Benutzers
  # @param password [String] Passwort des technischen Benutzers
  #
  def initialize(host:, username:, password:)
    @host = host
    @username = username
    @password = password
  end

  # Importiert eine neue Publikation in Librario
  #
  # @param data [Hash] Publikationsdaten
  # @option data [String] :title Titel der Publikation (Pflichtfeld)
  # @option data [String] :publishable_type Publikationstyp: 'Book', 'Report', 'Article', etc.
  # @option data [Array<Hash>] :authors Liste der Autoren mit :name und :type ('person' oder 'corporate')
  # @option data [String] :isbn ISBN-Nummer (optional)
  # @option data [String] :doi DOI-Nummer (optional)
  # @option data [String] :published_on Publikationsdatum im Format 'YYYY-MM-DD'
  # @option data [Array<String>] :categories Liste der Kategorien
  #
  # @return [Hash, nil] Die erstellte Publikation mit ID oder nil bei Fehler
  #
  # @example Publikation mit allen Feldern
  #   importer.import_publication({
  #     title: 'Handbuch der Informatik',
  #     publishable_type: 'Book',
  #     authors: [
  #       { name: 'Dr. Anna Meyer', type: 'person' },
  #       { name: 'TU München', type: 'corporate' }
  #     ],
  #     isbn: '978-3-86680-192-9',
  #     published_on: '2024-03-15',
  #     categories: ['Informatik', 'Lehrbuch']
  #   })
  #
  def import_publication(data)
    uri = URI("https://#{@host}/api/v2/publications")

    # Request vorbereiten
    request = Net::HTTP::Post.new(uri)
    request.basic_auth(@username, @password)
    request['Content-Type'] = 'application/json'
    request.body = data.to_json

    # Request ausführen
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    if response.is_a?(Net::HTTPSuccess)
      publication = JSON.parse(response.body)
      puts "✓ Importiert: #{data[:title]} (ID: #{publication['id']})"
      publication
    else
      puts "✗ Fehler bei #{data[:title]}: #{response.body}"
      nil
    end
  end

  # Hängt eine Datei an eine bestehende Publikation an
  #
  # Diese Methode führt den kompletten Upload-Workflow durch:
  # 1. MIME-Type Erkennung
  # 2. Presigned URL von der API anfordern
  # 3. Datei zu S3 hochladen
  # 4. Asset in Librario erstellen
  #
  # @param publication_id [Integer] ID der Publikation
  # @param file_path [String] Lokaler Pfad zur Datei
  # @param name [String, nil] Anzeigename in Librario (optional, Standard: Dateiname)
  #
  # @return [Hash, nil] Das erstellte Asset oder nil bei Fehler
  #
  # @example PDF anhängen
  #   importer.attach_file(
  #     publication_id: 123,
  #     file_path: '/dokumente/handbuch.pdf',
  #     name: 'Volltext'
  #   )
  #
  # @example Excel-Datei mit automatischer MIME-Type Erkennung
  #   importer.attach_file(
  #     publication_id: 123,
  #     file_path: '/daten/messwerte.xlsx',
  #     name: 'Messdaten Anhang'
  #   )
  #
  def attach_file(publication_id:, file_path:, name: nil)
    # Datei prüfen
    unless File.exist?(file_path)
      puts "✗ Datei nicht gefunden: #{file_path}"
      return nil
    end

    # MIME-Type automatisch ermitteln
    mime_type = MIME::Types.type_for(file_path).first&.content_type || 'application/octet-stream'
    file_name = File.basename(file_path)
    display_name = name || file_name

    puts "→ Lade Datei hoch: #{display_name} (#{mime_type})"

    # Presigned URL anfordern
    presign_data = get_presigned_url(filename: file_name, type: mime_type)
    return nil unless presign_data

    # Datei hochladen
    file_id = upload_to_s3(presign_data, file_path)
    return nil unless file_id

    # Asset erstellen
    create_asset(
      publication_id: publication_id,
      file_id: file_id,
      file_path: file_path,
      name: display_name,
      mime_type: mime_type
    )
  end

  private

  # Fordert eine Presigned URL für den S3-Upload an
  # @private
  def get_presigned_url(filename:, type:)
    uri = URI("https://#{@host}/api/v2/assets/presign")
    uri.query = URI.encode_www_form(filename: filename, type: type)

    request = Net::HTTP::Get.new(uri)
    request.basic_auth(@username, @password)

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    if response.is_a?(Net::HTTPSuccess)
      JSON.parse(response.body)
    else
      puts "✗ Fehler beim Anfordern der Upload-URL: #{response.body}"
      nil
    end
  end

  # Lädt die Datei zu S3 hoch
  # @private
  def upload_to_s3(presign_data, file_path)
    uri = URI(presign_data['url'])
    fields = presign_data['fields']

    # KRITISCH: File ID = S3 Key ohne "cache/" Präfix
    # Das Backend erwartet die Datei unter cache/{file.id}
    file_id = fields['key'].sub(%r{^cache/}, '')

    # Policy dekodieren für Content-Disposition
    # S3 validiert den exakten Wert aus der Policy
    policy = JSON.parse(Base64.decode64(fields['policy']))
    cd_value = policy['conditions'].find do |c|
      c.is_a?(Hash) && c['Content-Disposition']
    end&.dig('Content-Disposition')

    # Multipart-Request aufbauen
    boundary = "----RubyFormBoundary#{Time.now.to_i}"
    body = build_multipart_body(fields, file_path, cd_value, boundary)

    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
    request.body = body

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    if response.is_a?(Net::HTTPSuccess)
      # S3 gibt bei Erfolg Status 204 zurück
      file_id
    else
      puts "✗ Fehler beim Hochladen zu S3: #{response.code} #{response.body}"
      nil
    end
  end

  # Erstellt den Multipart-Body für S3-Upload
  # @private
  def build_multipart_body(fields, file_path, cd_override, boundary)
    parts = []

    # Form-Felder hinzufügen (Reihenfolge ist wichtig!)
    fields.each do |key, value|
      # Content-Disposition aus Policy verwenden für korrekte Validierung
      value = cd_override if key == 'Content-Disposition' && cd_override

      parts << "--#{boundary}"
      parts << "Content-Disposition: form-data; name=\"#{key}\""
      parts << ''
      parts << value
    end

    # Datei als letztes Feld
    parts << "--#{boundary}"
    parts << "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(file_path)}\""
    parts << "Content-Type: #{fields['Content-Type'] || 'application/octet-stream'}"
    parts << ''
    parts << File.binread(file_path) # Binärmodus für alle Dateitypen
    parts << "--#{boundary}--"

    # WICHTIG: \r\n als Zeilentrenner (HTTP-Standard)
    parts.join("\r\n")
  end

  # Erstellt das Asset in Librario und verknüpft es mit der Publikation
  # @private
  def create_asset(publication_id:, file_id:, file_path:, name:, mime_type:)
    uri = URI("https://#{@host}/api/v2/assets")

    request = Net::HTTP::Post.new(uri)
    request.basic_auth(@username, @password)
    request['Content-Type'] = 'application/json'
    request.body = {
      publication_id: publication_id,
      name: name,
      content_type: mime_type,
      file: {
        id: file_id,
        storage: 'cache',
        metadata: {
          size: File.size(file_path),
          filename: File.basename(file_path),
          mime_type: mime_type
        }
      }
    }.to_json

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    if response.is_a?(Net::HTTPSuccess)
      asset = JSON.parse(response.body)
      puts "✓ Datei '#{name}' erfolgreich angehängt (ID: #{asset['id']})"
      asset
    else
      puts "✗ Fehler beim Anhängen der Datei '#{name}': #{response.body}"
      nil
    end
  end
end

# ============================================================================
# Beispiel-Verwendung
# ============================================================================

# Importer initialisieren
importer = LibrarioImport.new(
  host: 'ihre-firma.mylibrar.io',
  username: 'tech_user@example.com',
  password: 'ihr_passwort'
)

# Publikation mit umfangreichen Metadaten erstellen
publication = importer.import_publication({
                                            title: 'Technische Dokumentation 2024',
                                            publishable_type: 'Report',
                                            published_on: '2024-01-15',
                                            authors: [
                                              { name: 'Dr. Maria Schmidt', type: 'person' },
                                              { name: 'Prof. Hans Weber', type: 'person' },
                                              { name: 'Forschungsinstitut GmbH', type: 'corporate' }
                                            ],
                                            categories: %w[Technik Forschungsbericht],
                                            abstract: 'Eine umfassende technische Dokumentation...'
                                          })

if publication
  # PDF-Volltext hinzufügen
  importer.attach_file(
    publication_id: publication['id'],
    file_path: 'dokument.pdf',
    name: 'Hauptdokument'
  )

  # Excel-Datei mit Messdaten (MIME-Type wird automatisch erkannt)
  importer.attach_file(
    publication_id: publication['id'],
    file_path: 'messdaten.xlsx',
    name: 'Messdaten Anhang'
  )
end

Häufige Fehlerquellen

  • Policy-Validierung: Die Content-Disposition muss exakt dem Wert in der S3-Policy entsprechen
  • File ID Format: Verwenden Sie file_id = key.sub(/^cache\//, '') statt komplexere Extraktion
  • Multipart-Encoding: Zeilenumbrüche müssen \r\n sein, nicht nur \n

Automatischer Abgleich von Publikationslisten

Ein häufiges Szenario ist der Abgleich von EndNote-Exporten mit dem Librario-Bestand. Dabei soll ermittelt werden, welche Publikationen bereits verfügbar sind und welche noch beschafft werden müssen.

# Einzelne DOI prüfen
curl -G "https://ihre-firma.mylibrar.io/api/v2/publications" \
  -u "tech_user@example.com:password" \
  --data-urlencode "doi=10.1234/example.doi"
Voraussetzungen für die Input-Datei
  • CSV-Datei mit UTF-8 Kodierung
  • Enthält eine Spalte mit dem Header "DOI"
  • Weitere Spalten werden in die Ausgabedatei übernommen

Beispiel endnote_export.csv:

DOI,Title,Authors,Year
10.1234/example.1,Example Paper 1,John Doe,2023
10.1234/example.2,Example Paper 2,Jane Smith,2022

Format der Ausgabedatei

Die Ausgabedatei availability_report.csv enthält alle Spalten der Eingabedatei plus zwei zusätzliche Spalten:

  • Status: "Verfügbar" oder "Nicht verfügbar"
  • URL: Link zur Publikation in Librario (falls verfügbar)

Beispiel availability_report.csv:

DOI,Title,Authors,Year,Status,URL
10.1234/example.1,Example Paper 1,John Doe,2023,Verfügbar,https://firma.mylibrar.io/publications/123
10.1234/example.2,Example Paper 2,Jane Smith,2022,Nicht verfügbar,

automatischer-abgleich.rb
require 'net/http'
require 'json'
require 'uri'
require 'csv'

class PublicationChecker
  def initialize(host:, username:, password:)
    @host = host
    @username = username
    @password = password
  end

  def check_publications(input_file:, output_file:)
    # Eingabe-CSV lesen
    rows = CSV.read(input_file, headers: true)

    # Für jede DOI den Status prüfen
    rows.each do |row|
      next unless row['DOI'] # Zeilen ohne DOI überspringen

      uri = URI("https://#{@host}/api/v2/publications")
      uri.query = URI.encode_www_form(doi: row['DOI'])

      # Request vorbereiten & ausführen
      request = Net::HTTP::Get.new(uri)
      request.basic_auth(@username, @password)

      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
        http.request(request)
      end

      data = JSON.parse(response.body)

      if response.is_a?(Net::HTTPSuccess) && !data['records'].empty?
        # Publikation gefunden
        pub = data['records'].first
        row['Status'] = 'Verfügbar'
        row['URL'] = "https://#{@host}/publications/#{pub['id']}"
      else
        # Nicht im Bestand
        row['Status'] = 'Nicht verfügbar'
        row['URL'] = ''
      end
    end

    # Ergebnis in neue CSV schreiben
    CSV.open(output_file, 'w') do |csv|
      csv << rows.headers # Headers inkl. neue Spalten
      rows.each { |row| csv << row }
    end
  end
end

# Verwendung
checker = PublicationChecker.new(
  host: 'ihre-firma.mylibrar.io',
  username: 'tech_user@example.com',
  password: 'password'
)

checker.check_publications(
  input_file: 'endnote_export.csv',
  output_file: 'availability_report.csv'
)

Backups und Datensicherung

Die API bietet umfassende Möglichkeiten zur Datensicherung. Ein einfaches Backup mittels /publications/all Endpunkt ermöglicht das seitenweise Abrufen aller Publikationen. Weitere Details finden Sie in der Backup-Dokumentation.

Best Practices

Rate Limiting

Um die API-Server nicht zu überlasten, beachten Sie bitte:

  • Maximale Anfragerate: 60 Anfragen pro Minute
  • Bei Listenabfragen: Paginierung verwenden
  • Große Datenmengen in kleineren Batches verarbeiten

Fehlerbehandlung

Die API verwendet folgende HTTP-Statuscodes:

  • 200: Anfrage erfolgreich
  • 401: Nicht autorisiert
  • 403: Zugriff verweigert
  • 404: Ressource nicht gefunden
  • 429: Zu viele Anfragen (Rate Limit überschritten)
  • 500: Interner Serverfehler

Fehlerantworten enthalten zusätzliche Details im JSON-Format:

{
  "error": {
    "code": "RecordNotFound",
    "message": "Requested record not found"
  }
}