Developer Tutorials

Komplet Bluestone PIM API Integration: Autentificering, Attributter & Produkter med TypeScript

Omfattende tutorial til Bluestone PIM API integration. Lær autentificering, opret attributtyper, styr produkter og byg komplette PIM workflows med TypeScript eksempler.

Udgivet 15. januar 2025
20 min læsning
Sivert Kjøller Bertelsen
Bluestone
API
TypeScript
Tutorial
Integration
MACH
Autentificering

Hvad du vil lære

Denne tutorial guider dig gennem integration med Bluestone PIM API, fra initial autentificering til avanceret produktsøgningsfunktionalitet. Du vil bygge en komplet TypeScript applikation, der demonstrerer API-brugsmønstre fra den virkelige verden.

Bluestones MACH-certificerede API arkitektur giver kraftfulde søgekapabiliteter gennem Elasticsearch, omfattende autentificering via MAPI credentials og fleksible dataadgangsmønstre. Denne tutorial dækker både succesfulde integrationsmønstre og de tilladelsesbaserede begrænsninger, du måske støder på.

Tutorial Resultater

  • Autentificer med Bluestone MAPI credentials
  • Sæt TypeScript projekt op med auto-genereret SDK
  • Implementer produktsøgning ved hjælp af Elasticsearch API
  • Håndter autentifikationstokens og automatisk fornyelse
  • Byg genanvendelige API klientmønstre
  • Forstå API tilladelser og adgangsniveauer

Forudsætninger

Nødvendige Credentials

Før du starter, skal du sikre dig, at du har adgang til et Bluestone PIM testmiljø med følgende credentials:

MAPI Credentials Påkrævet

  • Organisationsnavn (f.eks. 'SANDBOX - POC')
  • Miljøbetegnelse (TEST/PRODUCTION)
  • PAPI-nøgle til API adgang
  • MAPI Client ID til OAuth2 autentificering
  • MAPI Client Secret til sikker tokengenerering

Udviklingsmiljø

  • Node.js 16+ med npm/yarn
  • TypeScript udviklingssetup
  • Kodeeditor med TypeScript support
  • Grundlæggende forståelse af REST API'er og OAuth2
  • Kendskab til async/await mønstre

Kom i gang

Trin 1: Initialiser dit projekt

Opret et nyt Node.js projekt og installer de nødvendige dependencies til TypeScript udvikling og Bluestone API integration.

Projekt Initialisering

Sæt et nyt TypeScript projekt op med påkrævede dependencies

bash
# Initialiser nyt Node.js projekt
npm init -y

# Installer Bluestone SDK ved hjælp af officiel generator
npx api install "@docs-api-test-bspim/v1-core#dzoga1clxllapfw" --yes

# Installer TypeScript udviklingsdependencies
npm install -D typescript @types/node ts-node

# Opret TypeScript konfiguration
echo '{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "baseUrl": "."
  },
  "include": ["src/**/*", ".api/**/*"],
  "exclude": ["node_modules", "dist"]
}' > tsconfig.json

# Opret source mappe
mkdir src
"Selvom denne tutorial fokuserer på TypeScript implementering, giver Bluestones API reference omfattende eksempler på tværs af 20+ programmeringssprog inklusive C#/.NET, Python, Java, PHP, Ruby og Go. De grundlæggende autentificerings- og API-mønstre forbliver konsistente uanset dit valgte sprog."
SB
Sivert Kjøller Bertelsen
API Integrationsspecialist

Trin 2: Implementer autentificering

Bluestone bruger OAuth2 client credentials flow til API autentificering. MAPI credentials genererer bearer tokens, der udløber efter 3600 sekunder (1 time).

Forståelse af Bluestone autentificering

Autentificeringsprocessen involverer at poste dine client credentials til identity provider endpoint for at modtage et bearer token. Dette token skal inkluderes i alle efterfølgende API requests som en Authorization header.

For komplet API dokumentation inkluderende alle tilgængelige endpoints, autentificeringsdetaljer og request/response schemas, se Bluestone OpenAPI Dokumentation.

Autentificering Implementering

Komplet autentificeringsmodul med credential management og token håndtering

typescript
import docsApiTestBspim from '../.api/apis/docs-api-test-bspim';

// Bluestone API Credentials
const CREDENTIALS = {
  // Erstat med dine faktiske credentials
  clientId: 'your-client-id-here',
  clientSecret: 'your-client-secret-here',
  grantType: 'client_credentials'
};

async function authenticateWithBluestoneAPI() {
  try {
    console.log('🔐 Autentificerer med Bluestone API...');
    console.log('📍 Organisation: Din Organisation');
    console.log('🌍 Miljø: TEST');
    
    // Generer token ved hjælp af MAPI credentials
    const response = await docsApiTestBspim.generateToken({
      client_id: CREDENTIALS.clientId,
      client_secret: CREDENTIALS.clientSecret,
      grant_type: CREDENTIALS.grantType
    });

    console.log('✅ Autentificering succesfuld!');
    
    if (response.data && response.data.access_token) {
      console.log('🎟️  Access Token opnået');
      console.log('⏱️  Udløber om:', response.data.expires_in, 'sekunder');
      console.log('🏷️  Token Type:', response.data.token_type);
      
      return {
        accessToken: response.data.access_token,
        expiresIn: response.data.expires_in,
        tokenType: response.data.token_type
      };
    }
    
  } catch (error) {
    console.error('❌ Autentificering fejlede:', error);
    throw error;
  }
}

export default authenticateWithBluestoneAPI;
export { CREDENTIALS };

Trin 3: Byg API klient med token management

Opret en genanvendelig API klient, der håndterer autentificering, token validering og automatisk fornyelse. Dette mønster sikrer, at din applikation opretholder gyldig autentificering gennem hele dens livscyklus.

Nøglefunktioner

API klienten implementerer automatisk token validering, håndterer udløbne tokens ved at re-autentificere og giver en ren interface til at lave autentificerede requests til forskellige Bluestone API endpoints.

API Klient med Token Management

Genanvendelig API klient der håndterer autentificering og token livscyklus

typescript
import docsApiTestBspim from '../.api/apis/docs-api-test-bspim';
import authenticateWithBluestoneAPI from './authenticate';

interface AuthenticatedApiClient {
  accessToken: string;
  expiresIn: number;
  tokenType: string;
  authenticatedAt: Date;
}

class BluestoneApiClient {
  private client: AuthenticatedApiClient | null = null;

  async initialize(): Promise<void> {
    console.log('🚀 Initialiserer Bluestone API Klient...');
    
    const authInfo = await authenticateWithBluestoneAPI();
    
    if (authInfo && authInfo.accessToken && authInfo.expiresIn && authInfo.tokenType) {
      this.client = {
        accessToken: authInfo.accessToken,
        expiresIn: authInfo.expiresIn,
        tokenType: authInfo.tokenType,
        authenticatedAt: new Date()
      };
      
      // Konfigurer SDK'et med bearer token
      docsApiTestBspim.auth(authInfo.accessToken);
      
      console.log('✅ API Klient initialiseret og autentificeret!');
    } else {
      throw new Error('Fejlede med at autentificere med Bluestone API');
    }
  }

  isTokenValid(): boolean {
    if (!this.client) return false;
    
    const now = new Date();
    const elapsed = (now.getTime() - this.client.authenticatedAt.getTime()) / 1000;
    const isValid = elapsed < this.client.expiresIn;
    
    if (!isValid) {
      console.log('⚠️  Token er udløbet, re-autentificering påkrævet');
    }
    
    return isValid;
  }

  getTokenInfo(): AuthenticatedApiClient | null {
    return this.client;
  }

  displayStatus(): void {
    if (!this.client) {
      console.log('❌ API Klient ikke initialiseret');
      return;
    }

    const now = new Date();
    const elapsed = (now.getTime() - this.client.authenticatedAt.getTime()) / 1000;
    const remaining = this.client.expiresIn - elapsed;

    console.log('\n📊 Bluestone API Klient Status:');
    console.log(`🔑 Token Type: ${this.client.tokenType}`);
    console.log(`⏰ Autentificeret på: ${this.client.authenticatedAt.toISOString()}`);
    console.log(`⏱️  Tid tilbage: ${Math.max(0, Math.floor(remaining))} sekunder`);
    console.log(`✅ Token gyldig: ${this.isTokenValid() ? 'Ja' : 'Nej'}`);
  }
}

export default BluestoneApiClient;

Trin 4: Implementer produktsøgning

Bluestone giver kraftfulde produktsøgningskapabiliteter gennem Elasticsearch. Søge-API'et understøtter komplekse queries, filtrering, sortering og paginering - essentielt for at bygge robuste PIM integrationer.

Forståelse af Bluestone API Endpoints

Baseret på virkelige tests har forskellige API endpoints varierende tilladelseskrav. Elasticsearch søge-endpoint har typisk bredere adgang end detaljerede produktstyringsendpoints.

Produktsøgning Implementering

Komplet produktsøgningsklient med Elasticsearch support

typescript
import BluestoneApiClient from './api-client';

// Bluestone API URLs til forskellige services
const API_ENDPOINTS = {
  MANAGEMENT_API: 'https://api.test.bluestonepim.com',
  SEARCH_API: 'https://api.test.bluestonepim.com/search'
};

interface ElasticsearchQuery {
  query?: any;
  size?: number;
  from?: number;
  sort?: any[];
}

class ProductSearchClient extends BluestoneApiClient {
  
  /**
   * Søg efter produkter ved hjælp af Elasticsearch query
   */
  async searchProductsElasticsearch(elasticsearchQuery: ElasticsearchQuery): Promise<any> {
    await this.ensureAuthenticated();
    
    console.log('🔍 Søger produkter med Elasticsearch query:', elasticsearchQuery);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        `${API_ENDPOINTS.SEARCH_API}/products/search`,
        'POST',
        elasticsearchQuery
      );
      
      console.log('✅ Elasticsearch produktsøgning succesfuld!');
      return response;
      
    } catch (error) {
      console.error('❌ Elasticsearch produktsøgning fejlede:', error);
      throw error;
    }
  }

  /**
   * Sikr at vi har et gyldigt autentifikationstoken
   */
  protected async ensureAuthenticated(): Promise<void> {
    if (!this.isTokenValid()) {
      console.log('🔄 Re-autentificerer...');
      await this.initialize();
    }
  }

  /**
   * Lav et autentificeret HTTP request til Bluestone API
   */
  protected async makeAuthenticatedAPIRequest(
    url: string, 
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
    body?: any
  ): Promise<any> {
    const tokenInfo = this.getTokenInfo();
    
    if (!tokenInfo) {
      throw new Error('Intet autentifikationstoken tilgængeligt');
    }

    const headers: Record<string, string> = {
      'Authorization': `${tokenInfo.tokenType} ${tokenInfo.accessToken}`,
      'Content-Type': 'application/json'
    };

    const requestOptions: RequestInit = {
      method,
      headers
    };

    if (body && (method === 'POST' || method === 'PUT')) {
      requestOptions.body = JSON.stringify(body);
    }

    console.log(`📡 Laver ${method} request til: ${url}`);
    
    const response = await fetch(url, requestOptions);
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`HTTP ${response.status}: ${errorText}`);
    }

    return await response.json();
  }
}

export default ProductSearchClient;

Trin 5: Attributstyring

Ud over grundlæggende produktsøgning giver Bluestone PIM omfattende attributstyringskapabiliteter. Du kan oprette brugerdefinerede attributtyper, organisere dem i grupper og tildele værdier til produkter - essentielt for at bygge robuste produktinformationsstyringsworkflows.

Attributsystem Oversigt

Bluestones attributsystem består af tre hovedkomponenter:

Attributsystem Komponenter

  • <strong>Attributgrupper</strong> - Organiser relaterede attributter sammen
  • <strong>Attributdefinitioner</strong> - Definer strukturen og datatypen af attributter
  • <strong>Attributværdier</strong> - Tildel faktiske værdier til produkter for hver attribut

Understøttede Datatyper (11 Funktionelle Typer)

  • Tekstfelter til navne og beskrivelser
  • Numre (heltal og decimaler) med enheder
  • Datoer, tider og tidsstempler
  • Boolean felter til flag og status
  • Mønstervalidering til strukturerede data
  • Rig formateret tekst med HTML
  • Lokationsfelter til adresser

Oprettelse af Attributgrupper

Organiser relaterede attributter ved først at oprette attributgrupper

typescript
import ProductSearchClient from './product-search';

class AttributeManagementClient extends ProductSearchClient {
  
  /**
   * Opret en attributgruppe for at organisere relaterede attributter
   */
  async createAttributeGroup(groupData: {
    name: string;
    description: string;
    metadata?: any;
  }): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('📁 Opretter attributgruppe:', groupData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/attribute-groups',
        'POST',
        groupData
      );
      
      // Udtræk gruppe ID fra Location header
      const groupId = response.headers?.get?.('resource-id') || 
                     response.headers?.get?.('location')?.split('/').pop();
      
      if (groupId) {
        console.log('✅ Attributgruppe oprettet med ID:', groupId);
        return groupId;
      } else {
        console.warn('⚠️  Gruppe oprettet men ID ikke fundet i headers');
        return null;
      }
      
    } catch (error) {
      if (error.message.includes('409')) {
        console.log('ℹ️  Attributgruppe eksisterer allerede:', groupData.name);
        return null; // Håndter konflikt elegant
      }
      console.error('❌ Fejlede med at oprette attributgruppe:', error);
      throw error;
    }
  }

  /**
   * Override base request metoden for at håndtere headers korrekt
   */
  protected async makeAuthenticatedAPIRequest(
    url: string, 
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
    body?: any
  ): Promise<any> {
    const tokenInfo = this.getTokenInfo();
    
    if (!tokenInfo) {
      throw new Error('Intet autentifikationstoken tilgængeligt');
    }

    const headers: Record<string, string> = {
      'Authorization': `${tokenInfo.tokenType} ${tokenInfo.accessToken}`,
      'Content-Type': 'application/json'
    };

    const requestOptions: RequestInit = {
      method,
      headers
    };

    if (body && (method === 'POST' || method === 'PUT')) {
      requestOptions.body = JSON.stringify(body);
    }

    console.log(`📡 Laver ${method} request til: ${url}`);
    
    const response = await fetch(url, requestOptions);
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`HTTP ${response.status}: ${errorText}`);
    }

    // Returner response objekt med headers til resource ID udtrækning
    return {
      data: response.status === 204 ? null : await response.json().catch(() => null),
      headers: response.headers
    };
  }
}

export default AttributeManagementClient;

Trin 6: Oprettelse af Attributdefinitioner

Attributdefinitioner specificerer datatypen, valideringsregler og strukturen for produktattributter. Bluestone understøtter 11 fuldt funktionelle attributtyper, der dækker de fleste PIM use cases.

Funktionelle Attributtyper

Baseret på omfattende tests giver disse attributtyper komplet end-to-end funktionalitet:

Attributdefinition Oprettelseseksempler

Opret forskellige typer af attributdefinitioner med korrekt validering

typescript
class AttributeDefinitionClient extends AttributeManagementClient {
  
  /**
   * Opret en attributdefinition
   */
  async createAttributeDefinition(attributeData: any): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('🏷️  Opretter attributdefinition:', attributeData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/attribute-definitions',
        'POST',
        attributeData
      );
      
      const definitionId = response.headers?.get('resource-id');
      
      if (definitionId) {
        console.log('✅ Attributdefinition oprettet med ID:', definitionId);
        return definitionId;
      }
      
      return null;
      
    } catch (error) {
      if (error.message.includes('409')) {
        console.log('ℹ️  Attributdefinition eksisterer allerede:', attributeData.name);
        return null;
      }
      console.error('❌ Fejlede med at oprette attributdefinition:', error);
      throw error;
    }
  }

  /**
   * Få attributdefinitionsskabeloner for alle funktionelle typer
   */
  getAttributeTemplates(groupId: string) {
    return {
      // 1. Grundlæggende tekstfelt
      text: {
        name: 'Produkttitel',
        dataType: 'text',
        description: 'Grundlæggende produkttitelfelt',
        groupId: groupId
      },

      // 2. Multi-linje tekst
      multiline: {
        name: 'Produktbeskrivelse',
        dataType: 'multiline',
        description: 'Detaljeret produktbeskrivelsefelt',
        groupId: groupId
      },

      // 3. Mønstervalidering (email eksempel)
      pattern: {
        name: 'Kontakt Email',
        dataType: 'pattern',
        description: 'Email adresse med validering',
        groupId: groupId,
        restrictions: {
          text: {
            pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
          }
        }
      },

      // 4. Boolean felt
      boolean: {
        name: 'Er Fremhævet Produkt',
        dataType: 'boolean',
        description: 'Marker produkt som fremhævet',
        groupId: groupId
      },

      // 5. Decimal tal med enhed
      decimal: {
        name: 'Produktvægt',
        dataType: 'decimal',
        unit: 'kg',
        description: 'Produktvægt i kilogram',
        groupId: groupId
      },

      // 6. Heltal
      integer: {
        name: 'Lager Mængde',
        dataType: 'integer',
        description: 'Tilgængelig lagermængde',
        groupId: groupId
      },

      // 7. Datofelt
      date: {
        name: 'Lanceringsdato',
        dataType: 'date',
        description: 'Produkt lanceringsdato',
        groupId: groupId
      },

      // 8. DateTime felt
      dateTime: {
        name: 'Sidst Opdateret',
        dataType: 'date_time',
        description: 'Sidste ændringsstempel',
        groupId: groupId
      },

      // 9. Tidsfelt
      time: {
        name: 'Behandlingstid',
        dataType: 'time',
        description: 'Påkrævet behandlingstid',
        groupId: groupId
      },

      // 10. Lokationsfelt
      location: {
        name: 'Oprindelseslokation',
        dataType: 'location',
        description: 'Produkt oprindelseslokation',
        groupId: groupId
      },

      // 11. Formateret tekst (HTML)
      formattedText: {
        name: 'Marketingindhold',
        dataType: 'formatted_text',
        description: 'Rigt marketingindhold med formatering',
        groupId: groupId
      }
    };
  }

  /**
   * Opret et komplet sæt af almindelige produktattributter
   */
  async createProductAttributeSchema(groupId: string): Promise<Record<string, string>> {
    const templates = this.getAttributeTemplates(groupId);
    const attributeIds: Record<string, string> = {};
    
    console.log('🏗️  Opretter komplet produktattributskema...');
    
    // Opret hver attributtype med delay for at respektere rate limits
    for (const [key, template] of Object.entries(templates)) {
      try {
        const definitionId = await this.createAttributeDefinition(template);
        if (definitionId) {
          attributeIds[key] = definitionId;
        }
        
        // Tilføj delay mellem oprettelseskald
        await new Promise(resolve => setTimeout(resolve, 200));
        
      } catch (error) {
        console.error(`Fejlede med at oprette ${key} attribut:`, error);
      }
    }
    
    console.log(`✅ Oprettede ${Object.keys(attributeIds).length} attributdefinitioner`);
    return attributeIds;
  }
}

export default AttributeDefinitionClient;

Trin 7: Produktoprettelse og Attributtildeling

Med din attributstruktur på plads kan du nu oprette produkter og tildele attributværdier. Dette demonstrerer det komplette PIM workflow fra skemadefinition til datapopulering.

Produktoprettelsesproces

Produktoprettelse følger en to-trins proces: først opret produktet med grundlæggende information, derefter tildel attributværdier ved hjælp af produkt ID og attributdefinitions ID'er.

Produktoprettelse og Attributtildeling

Komplet workflow til oprettelse af produkter og tildeling af attributværdier

typescript
class ProductManagementClient extends AttributeDefinitionClient {
  
  /**
   * Opret et nyt produkt
   */
  async createProduct(productData: {
    sku: string;
    name: string;
    description?: string;
  }): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('📦 Opretter produkt:', productData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/products',
        'POST',
        productData
      );
      
      // Udtræk produkt ID fra Location header
      const productId = response.headers?.get('location')?.split('/').pop() ||
                       response.headers?.get('resource-id');
      
      if (productId) {
        console.log('✅ Produkt oprettet med ID:', productId);
        return productId;
      }
      
      return null;
      
    } catch (error) {
      console.error('❌ Fejlede med at oprette produkt:', error);
      throw error;
    }
  }

  /**
   * Tildel attributværdier til et produkt
   */
  async assignProductAttributes(
    productId: string, 
    attributeValues: Array<{ definitionId: string; value: any }>
  ): Promise<boolean> {
    await this.ensureAuthenticated();
    
    console.log(`🏷️  Tildeler ${attributeValues.length} attributter til produkt ${productId}`);

    try {
      await this.makeAuthenticatedAPIRequest(
        `https://api.test.bluestonepim.com/pim/products/${productId}/attributes`,
        'POST',
        attributeValues
      );
      
      console.log('✅ Attributværdier tildelt succesfuldt');
      return true;
      
    } catch (error) {
      console.error('❌ Fejlede med at tildele attributværdier:', error);
      throw error;
    }
  }

  /**
   * Få eksempel attributværdier til test
   */
  getSampleAttributeValues(attributeIds: Record<string, string>) {
    return [
      // Tekstværdi
      { 
        definitionId: attributeIds.text, 
        value: 'Fantastisk Eksempelprodukt' 
      },
      
      // Multiline tekst
      { 
        definitionId: attributeIds.multiline, 
        value: 'Dette er en detaljeret\nproduktbeskrivelse med\nflere linjer af indhold.' 
      },
      
      // Email mønster
      { 
        definitionId: attributeIds.pattern, 
        value: 'kontakt@eksempel.dk' 
      },
      
      // Boolean værdi
      { 
        definitionId: attributeIds.boolean, 
        value: true 
      },
      
      // Decimal med enhed
      { 
        definitionId: attributeIds.decimal, 
        value: 2.5 
      },
      
      // Heltal værdi
      { 
        definitionId: attributeIds.integer, 
        value: 150 
      },
      
      // Dato værdi
      { 
        definitionId: attributeIds.date, 
        value: '2024-06-01' 
      },
      
      // DateTime værdi
      { 
        definitionId: attributeIds.dateTime, 
        value: '2024-01-15T10:30:00Z' 
      },
      
      // Tid værdi
      { 
        definitionId: attributeIds.time, 
        value: '14:30:00' 
      },
      
      // Lokation værdi
      { 
        definitionId: attributeIds.location, 
        value: 'Oslo, Norge' 
      },
      
      // Formateret tekst med HTML
      { 
        definitionId: attributeIds.formattedText, 
        value: '<h2>Produktfunktioner</h2><p>Dette produkt inkluderer <strong>fantastiske</strong> funktioner og <em>premium</em> kvalitet.</p>' 
      }
    ].filter(attr => attr.definitionId); // Inkluder kun attributter der blev oprettet succesfuldt
  }

  /**
   * Komplet produktoprettelsesworkflow
   */
  async createCompleteProduct(productInfo: {
    sku: string;
    name: string;
    description?: string;
  }, attributeIds: Record<string, string>): Promise<string | null> {
    
    console.log('🚀 Starter komplet produktoprettelsesworkflow...');
    
    try {
      // Trin 1: Opret produktet
      const productId = await this.createProduct(productInfo);
      
      if (!productId) {
        throw new Error('Fejlede med at oprette produkt - intet ID returneret');
      }
      
      // Trin 2: Forbered attributværdier
      const attributeValues = this.getSampleAttributeValues(attributeIds);
      
      if (attributeValues.length === 0) {
        console.warn('⚠️  Ingen attributværdier at tildele');
        return productId;
      }
      
      // Trin 3: Tildel attributværdier
      await this.assignProductAttributes(productId, attributeValues);
      
      console.log('🎉 Komplet produkt oprettet succesfuldt!');
      console.log(`📋 Produkt ID: ${productId}`);
      console.log(`🏷️  Attributter tildelt: ${attributeValues.length}`);
      
      return productId;
      
    } catch (error) {
      console.error('💥 Komplet produktoprettelse fejlede:', error);
      throw error;
    }
  }

  /**
   * Valider attributværdiformat før tildeling
   */
  validateAttributeValue(dataType: string, value: any): boolean {
    switch (dataType) {
      case 'boolean':
        return typeof value === 'boolean';
      case 'integer':
        return Number.isInteger(value);
      case 'decimal':
        return typeof value === 'number' && !isNaN(value);
      case 'date':
        return /^\d{4}-\d{2}-\d{2}$/.test(value);
      case 'date_time':
        return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z?$/.test(value);
      case 'time':
        return /^\d{2}:\d{2}:\d{2}$/.test(value);
      case 'pattern':
        return typeof value === 'string'; // Mønstervalidering sker server-side
      default:
        return typeof value === 'string';
    }
  }
}

export default ProductManagementClient;

Trin 8: Komplette Integrationseksempler

Lad os nu sætte det hele sammen med omfattende eksempler, der demonstrerer det komplette Bluestone PIM integrationsworkflow fra autentificering gennem produktoprettelse med attributter.

Komplette Integrationseksempler

Omfattende eksempler der viser det komplette PIM workflow fra attributter til produkter

typescript
import ProductManagementClient from './product-management';

async function demonstrateCompleteBluestoneIntegration() {
  const client = new ProductManagementClient();
  
  try {
    // Initialiser og autentificer
    console.log('🚀 Initialiserer Bluestone PIM Integration...');
    await client.initialize();
    
    // Eksempel 1: Opret attributstruktur
    console.log('\n1️⃣ Opretter Attributstruktur');
    
    // Trin 1: Opret attributgruppe
    const groupId = await client.createAttributeGroup({
      name: 'Produktinformation',
      description: 'Grundlæggende produktinformationsattributter',
      metadata: {
        source: 'api_integration_tutorial',
        category: 'core_data'
      }
    });
    
    if (!groupId) {
      console.log('ℹ️  Bruger eksisterende attributgruppe eller springer over...');
      return;
    }
    
    // Trin 2: Opret alle attributdefinitioner
    console.log('\n📋 Opretter attributdefinitioner...');
    const attributeIds = await client.createProductAttributeSchema(groupId);
    
    console.log(`\n✅ Oprettede ${Object.keys(attributeIds).length} attributdefinitioner:`);
    Object.entries(attributeIds).forEach(([type, id]) => {
      console.log(`   ${type}: ${id}`);
    });
    
    // Eksempel 2: Opret produkt med attributter
    console.log('\n2️⃣ Opretter Komplet Produkt med Attributter');
    
    const productInfo = {
      sku: 'DEMO-PROD-001',
      name: 'Demo Produkt med Fulde Attributter',
      description: 'Et demonstrationsprodukt der viser alle attributtyper'
    };
    
    const productId = await client.createCompleteProduct(productInfo, attributeIds);
    
    if (productId) {
      console.log(`\n🎉 Oprettede succesfuldt komplet produkt: ${productId}`);
    }
    
    // Eksempel 3: Søg efter det oprettede produkt
    console.log('\n3️⃣ Søger efter Oprettet Produkt');
    const searchResults = await client.searchProductsElasticsearch({
      query: {
        bool: {
          must: [
            { term: { "sku": "DEMO-PROD-001" } }
          ]
        }
      },
      size: 1
    });
    
    if (searchResults && searchResults.data && searchResults.data.length > 0) {
      console.log('✅ Fandt oprettet produkt:');
      console.log(`   ID: ${searchResults.data[0].id}`);
      console.log(`   SKU: ${searchResults.data[0].sku}`);
      console.log(`   Navn: ${searchResults.data[0].name}`);
    }
    
    // Eksempel 4: Valider attributværdier
    console.log('\n4️⃣ Attributværdi Valideringseksempler');
    const testValues = [
      { type: 'boolean', value: true, expected: true },
      { type: 'boolean', value: 'true', expected: false },
      { type: 'integer', value: 42, expected: true },
      { type: 'integer', value: 3.14, expected: false },
      { type: 'date', value: '2024-01-15', expected: true },
      { type: 'date', value: '15/01/2024', expected: false },
      { type: 'date_time', value: '2024-01-15T10:30:00Z', expected: true },
      { type: 'time', value: '14:30:00', expected: true }
    ];
    
    testValues.forEach(test => {
      const isValid = client.validateAttributeValue(test.type, test.value);
      const status = isValid === test.expected ? '✅' : '❌';
      console.log(`   ${status} ${test.type}: ${JSON.stringify(test.value)} -> ${isValid}`);
    });
    
    // Eksempel 5: Bulk produktoprettelse
    console.log('\n5️⃣ Bulk Produktoprettelseseksempel');
    const bulkProducts = [
      { sku: 'BULK-001', name: 'Bulk Produkt 1' },
      { sku: 'BULK-002', name: 'Bulk Produkt 2' },
      { sku: 'BULK-003', name: 'Bulk Produkt 3' }
    ];
    
    const bulkResults = [];
    for (const product of bulkProducts) {
      try {
        const bulkProductId = await client.createProduct(product);
        if (bulkProductId) {
          bulkResults.push({ sku: product.sku, id: bulkProductId });
        }
        // Tilføj delay mellem bulk operationer
        await new Promise(resolve => setTimeout(resolve, 300));
      } catch (error) {
        console.error(`Fejlede med at oprette bulk produkt ${product.sku}:`, error.message);
      }
    }
    
    console.log(`✅ Oprettede ${bulkResults.length} bulk produkter`);
    
    // Vis endelig status
    client.displayStatus();
    
  } catch (error) {
    console.error('💥 Komplet integrationsdemonstration fejlede:', error);
  }
}

// Alternative fokuserede eksempler
async function demonstrateSpecificFeatures() {
  const client = new ProductManagementClient();
  await client.initialize();
  
  console.log('\n🎯 Fokuserede Funktionsdemonstrer');
  
  // Kun søgning eksempel
  console.log('\n🔍 Produktsøgningseksempler');
  const searchExamples = [
    {
      name: 'Match Alle Produkter',
      query: { query: { match_all: {} }, size: 5 }
    },
    {
      name: 'Nyeste Produkter',
      query: {
        query: { match_all: {} },
        size: 3,
        sort: [{ "_id": { "order": "desc" } }]
      }
    },
    {
      name: 'Produkter med Specifikke Felter',
      query: {
        query: {
          bool: {
            must: [
              { exists: { field: "sku" } },
              { exists: { field: "name" } }
            ]
          }
        },
        size: 3
      }
    }
  ];
  
  for (const example of searchExamples) {
    try {
      const results = await client.searchProductsElasticsearch(example.query);
      console.log(`   ${example.name}: ${results.data?.length || 0} produkter`);
    } catch (error) {
      console.error(`   ${example.name}: Fejlede - ${error.message}`);
    }
  }
}

// Kør demonstrationerne
if (require.main === module) {
  demonstrateCompleteBluestoneIntegration().then(() => {
    console.log('\n' + '='.repeat(50));
    return demonstrateSpecificFeatures();
  });
}

Succesfuldt Oprettede Attributværdier i Bluestone PIM

Screenshot der viser alle 11 attributtyper succesfuldt oprettet og udfyldt med værdier i Bluestone PIM interfacet. Dette demonstrerer den komplette end-to-end funktionalitet fra API integration til PIM system visning.

Package.json Scripts

Praktiske npm scripts til udvikling og test

json
{
  "name": "bluestone-api-integration",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc",
    "authenticate": "ts-node src/authenticate.ts",
    "search": "ts-node src/product-search.ts",
    "demo": "ts-node src/demo.ts",
    "dev": "ts-node src/demo.ts"
  },
  "dependencies": {
    "@api/docs-api-test-bspim": "file:.api/apis/docs-api-test-bspim"
  },
  "devDependencies": {
    "@types/node": "^22.15.29",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }
}

Trin 6: Kør din integration

Med din kode på plads kan du nu teste forskellige aspekter af Bluestone API integrationen ved hjælp af praktiske npm scripts.

Tilgængelige Kommandoer

  • `npm run authenticate` - Test grundlæggende autentificering
  • `npm run search` - Kør produktsøgningseksempler
  • `npm run demo` - Fuld demonstration med flere eksempler
  • `npm run build` - Kompiler TypeScript til JavaScript
  • `npm run dev` - Udviklingstilstand med auto-restart

Forventede Resultater

Når din integration fungerer korrekt, bør du se output lignende dette eksempel, der demonstrerer succesfuld autentificering og produktopdagelse.

Forventet Konsol Output

Eksempel på succesfulde API integrationsresultater

bash
🚀 Initialiserer Bluestone API Klient...
🔐 Autentificerer med Bluestone API...
📍 Organisation: SANDBOX - POC
🌍 Miljø: TEST
✅ Autentificering succesfuld!
🎟️  Access Token opnået
⏱️  Udløber om: 3600 sekunder
🏷️  Token Type: Bearer
✅ API Klient initialiseret og autentificeret!

1️⃣ Grundlæggende Produktsøgning (Match All)
🔍 Søger produkter med Elasticsearch query: { query: { match_all: {} }, size: 10 }
📡 Laver POST request til: https://api.test.bluestonepim.com/search/products/search
✅ Elasticsearch produktsøgning succesfuld!
✅ Fandt 6 produkter:
   1. ID: 67f3c4e7bb442e4f88e45c32
   2. ID: 67f3c4a810f2da33a7c88766
   3. ID: 67f3c47c36016948acd8d0dd
   4. ID: 67f3ba1cbb442e4f88e45958
   5. ID: 67f3a654bb442e4f88e4590d
   6. ID: 67f3a5d636016948acd8cc4b

2️⃣ Avanceret Boolean Query
✅ Avanceret query returnerede 6 produkter

📊 Bluestone API Klient Status:
🔑 Token Type: Bearer
⏰ Autentificeret på: 2025-01-15T12:06:07.957Z
⏱️  Tid tilbage: 3599 sekunder
✅ Token gyldig: Ja

Avancerede Elasticsearch Queries

Bluestones søge-API understøtter den fulde Elasticsearch Query DSL, hvilket muliggør sofistikerede produktopdagelsesmønstre. Her er almindelige query mønstre til PIM integrationer.

Avancerede Elasticsearch Query Eksempler

Almindelige query mønstre til produktsøgning og filtrering

typescript
// Eksempel query mønstre til forskellige brugssager

// 1. Match alle produkter (grundlæggende listing)
const matchAllQuery = {
  query: { match_all: {} },
  size: 20,
  from: 0
};

// 2. Boolean query med existence filter
const existenceQuery = {
  query: {
    bool: {
      must: [
        { exists: { field: "id" } },
        { exists: { field: "name" } }
      ]
    }
  },
  size: 10
};

// 3. Sortering og paginering
const sortedQuery = {
  query: { match_all: {} },
  size: 5,
  from: 0,
  sort: [
    { "_id": { "order": "desc" } },
    { "createdAt": { "order": "desc" } }
  ]
};

// 4. Term filtrering (når du kender specifikke værdier)
const termQuery = {
  query: {
    bool: {
      must: [
        { term: { "status": "active" } },
        { term: { "type": "product" } }
      ]
    }
  }
};

// 5. Range queries (til datoer, numre)
const rangeQuery = {
  query: {
    bool: {
      must: [
        {
          range: {
            "createdAt": {
              "gte": "2024-01-01",
              "lte": "2024-12-31"
            }
          }
        }
      ]
    }
  }
};

// Brugseksempel:
// const results = await client.searchProductsElasticsearch(matchAllQuery);

Fejlfinding af Almindelige Problemer

Autentificeringsfejl

Hvis autentificering fejler, skal du verificere at dine MAPI credentials er korrekte og at din organisation har API adgang aktiveret. Client ID og secret skal præcist matche det som Bluestone support har givet.

403 Forbidden Fejl

Nogle API endpoints kræver højere tilladelsesniveauer end andre. Søge-endpoints fungerer typisk med grundlæggende MAPI credentials, mens produktstyringsendpoints kan kræve yderligere tilladelser. Dette er by design for sikkerhed.

Token Udløb

Bearer tokens udløber efter 3600 sekunder. API klienten håndterer automatisk token fornyelse, men hvis du implementerer custom logik, skal du sikre at du tjekker token gyldighed før du laver requests.

Attribut-Specifikke Problemer

Baseret på omfattende tests er her almindelige attribut-relaterede problemer og deres løsninger:

Resource ID Udtrækning

Udtræk altid resource ID'er fra `resource-id` eller `location` headers, aldrig fra response body som typisk er tom for oprettelsesoperationer.

Attributværdi Formater

Hver attributtype har strenge formatkrav. Brug valideringsfunktionerne fra eksemplerne for at sikre korrekte datatyper før tildeling.

Mønster Regex Escaping

Ved brug af mønstervalideringsattributter skal du sikre korrekt JSON escaping af regex specialtegn (dobbelte backslashes).

Netværksforbindelse

Sikr at dit udviklingsmiljø kan nå Bluestone test endpoints. Nogle virksomhedsnetværk kan blokere ekstern API adgang.

Almindelige Fejlkoder

  • 401 Unauthorized - Ugyldige eller udløbne credentials
  • 403 Forbidden - Utilstrækkelige tilladelser til endpoint
  • 404 Not Found - Ugyldig endpoint URL eller ressource ikke fundet
  • 409 Conflict - Ressource eksisterer allerede (sikkert at ignorere for idempotente operationer)
  • 429 Rate Limited - For mange requests (standard: 500/minut)
  • 500 Internal Server Error - Bluestone service problem

Attribut Oprettelse Problemer

  • <strong>Feltnavn mismatch</strong> - Brug `definitionId` ikke `attributeDefinitionId` i værditildelinger
  • <strong>Forkerte datatyper</strong> - Boolean værdier skal være `true`/`false`, ikke strings
  • <strong>Datoformat fejl</strong> - Brug ISO formater: `YYYY-MM-DD` til datoer, `YYYY-MM-DDTHH:MM:SSZ` til datetime
  • <strong>Mønstervalidering</strong> - Dobbelt-escape regex specialtegn i JSON
  • <strong>Manglende gruppe ID'er</strong> - Opret altid attributgrupper før definitioner
  • <strong>Rate limiting</strong> - Tilføj delays mellem bulk attribut oprettelseskald

Integration Best Practices

Fejlhåndtering

Implementer altid ordentlig fejlhåndtering for API kald. Netværksproblemer, rate limiting og tilladelsesændringer kan forekomme i produktionsmiljøer.

Token Management

Opbevar tokens sikkert og implementer automatisk fornyelse. Log aldrig bearer tokens i produktionsmiljøer da de giver fuld API adgang.

Rate Limiting

Respekter Bluestones rate limits (standard 500 requests/minut). Implementer exponential backoff for rate-limiterede requests og overvej at cache responses når det er passende.

Miljøstyring

Brug miljøvariabler til credentials og API endpoints. Commit aldrig API nøgler til version control.

Monitorering

Implementer logging og monitorering for API integrationer. Spor autentificeringssuccesrater, responstider og fejlfrekvenser.

Produktionsovervejelser

  • Brug miljøvariabler til følsomme credentials
  • Implementer ordentlig logging uden at eksponere tokens
  • Tilføj retry logik med exponential backoff
  • Monitorer API forbrug og fejlrater
  • Cache responses når det er passende for at reducere API kald
  • Brug HTTPS til al API kommunikation

Klar til produktion?

Denne tutorial dækker det grundlæggende i Bluestone API integration. Til produktionsimplementeringer kan du overveje at implementere avancerede funktioner som webhook håndtering, bulk operationer og omfattende fejlgendannelse.

Lær om Bluestone PIM

Næste Skridt

Udvid din integration

Med grundlaget på plads kan du udvide din integration til at inkludere yderligere Bluestone API kapabiliteter som dine tilladelser tillader.

Integrationsudvidelser

  • Implementer webhook handlers til real-time data sync
  • Tilføj support for bulk produktoperationer
  • Integrer med Bluestones workflow og task API'er
  • Byg asset management funktionalitet ved hjælp af DAM API
  • Tilføj support for kategori- og attributstyring
  • Implementer dataeksport og synkroniseringsworkflows

Produktionsparathed

  • Sæt ordentlig miljøkonfiguration op
  • Implementer omfattende fejlhåndtering og retry logik
  • Tilføj monitorering og alarmer for API fejl
  • Opret automatiserede tests for din integration
  • Dokumenter dine API brugsmønstre til teamviden
  • Planlæg skalering og rate limit management

Kilder (1)

[1]
Bluestone API Reference
Bluestone(2025)API Dokumentation

Relaterede Artikler

Omfattende teknisk gennemgang af Bluestone PIM system inklusive MACH arkitektur, micro-services tilgang og implementeringsindsigter fra den virkelige verden.

Bluestone
MACH
PIM
Læs Artikel

Komplet guide til Product Information Management systemer. Lær hvad PIM er, hvordan det fungerer, vigtige fordele, og hvordan du vælger det rigtige PIM-system til din virksomhed.

PIM
Produktinformation
Guide
Læs Artikel