@@ -397,9 +397,19 @@ options.routes = [
397
397
398
398
## Plugins
399
399
400
- Plugins are a way to extend registry's context allowing components to inherit custom functionalities.
400
+ Plugins are a way to extend registry's context allowing components to inherit custom functionalities. They can be configured to receive component context information (name and version) when needed for enhanced security and decision-making capabilities.
401
401
402
- This is a plugin example:
402
+ ### Plugin Structure
403
+
404
+ A plugin consists of two main parts:
405
+
406
+ - ** register** : Function called during plugin initialization
407
+ - ** execute** : Function that provides the actual plugin functionality
408
+ - ** context** (optional): Boolean flag to enable component context awareness
409
+
410
+ ### Basic Plugin Example
411
+
412
+ This is a basic plugin example without context awareness:
403
413
404
414
``` js
405
415
// ./registry/oc-plugins/hobknob.js
@@ -418,53 +428,212 @@ module.exports.execute = function (featureName) {
418
428
};
419
429
```
420
430
421
- This is how to register it in a registry:
431
+ ### Context-Aware Plugin Example
432
+
433
+ When you need to make decisions based on which component is calling the plugin, you can enable context awareness:
434
+
435
+ ``` js
436
+ // ./registry/oc-plugins/feature-flags.js
437
+ var connection,
438
+ client = require (" ./feature-flags-client" );
439
+
440
+ module .exports .register = function (options , dependencies , next ) {
441
+ client .connect (options .connectionString , function (err , conn ) {
442
+ connection = conn;
443
+ next ();
444
+ });
445
+ };
446
+
447
+ // With context: true, execute receives component context and returns a function
448
+ module .exports .execute = function (context ) {
449
+ // context contains: { name: "component-name", version: "1.2.3" }
450
+ return function (featureName ) {
451
+ // Validate if this component is allowed to access this feature
452
+ if (
453
+ context .name .startsWith (" internal/" ) &&
454
+ featureName .startsWith (" public/" )
455
+ ) {
456
+ throw new Error (" Internal components cannot access public features" );
457
+ }
458
+
459
+ // Log feature access for audit purposes
460
+ console .log (
461
+ ` Component ${ context .name } @${ context .version } accessing feature: ${ featureName} `
462
+ );
463
+
464
+ return connection .get (featureName);
465
+ };
466
+ };
467
+
468
+ // Enable context awareness
469
+ module .exports .context = true ;
470
+ ```
471
+
472
+ ### Plugin Registration
473
+
474
+ This is how to register plugins in a registry:
422
475
423
476
``` js
424
477
// ./registry/init.js
425
- ...
478
+ var oc = require ( " oc " );
426
479
var registry = new oc.Registry (configuration);
427
480
481
+ // Register a basic plugin
428
482
registry .register ({
429
- name: ' getFeatureSwitch' ,
430
- register: require (' ./oc-plugins/hobknob' ),
483
+ name: " getFeatureSwitch" ,
484
+ register: require (" ./oc-plugins/hobknob" ),
431
485
options: {
432
- connectionString: connectionString
433
- }
486
+ connectionString: connectionString,
487
+ },
488
+ });
489
+
490
+ // Register a context-aware plugin
491
+ registry .register ({
492
+ name: " getSecureFeature" ,
493
+ register: require (" ./oc-plugins/feature-flags" ),
494
+ options: {
495
+ connectionString: connectionString,
496
+ },
434
497
});
435
- ...
436
498
```
437
499
438
- This is how to use a plugin from a component:
500
+ ### Using Plugins from Components
501
+
502
+ Components use plugins the same way regardless of whether they have context awareness:
439
503
440
504
``` js
441
505
// ./my-component/server.js
442
506
module .exports .data = function (context , callback ) {
443
507
callback (null , {
444
- variable: context .plugins .getFeatureSwitch (" AbTestHomePage" ),
508
+ // Basic plugin usage
509
+ basicFeature: context .plugins .getFeatureSwitch (" AbTestHomePage" ),
510
+
511
+ // Context-aware plugin usage (same interface)
512
+ secureFeature: context .plugins .getSecureFeature (" AdminPanel" ),
445
513
});
446
514
};
447
515
```
448
516
449
- This is how to depend on (and use) other plugins:
517
+ ### When to Use Context Awareness
518
+
519
+ Context awareness is useful when you need to:
520
+
521
+ 1 . ** Security Validation** : Restrict certain components from accessing specific features
522
+ 2 . ** Audit Logging** : Track which components are using which features
523
+ 3 . ** Component-Specific Logic** : Provide different behavior based on component name/version
524
+ 4 . ** Rate Limiting** : Apply different limits per component
525
+ 5 . ** Feature Rollouts** : Enable features only for specific components or versions
526
+
527
+ ### Advanced Context-Aware Plugin Example
528
+
529
+ Here's a more sophisticated example that demonstrates multiple use cases:
450
530
451
531
``` js
452
- // ./registry/oc-plugins/hobknob .js
532
+ // ./registry/oc-plugins/advanced-feature-manager .js
453
533
var connection,
454
- client = require (" ./hobknob-client" );
534
+ client = require (" ./feature-manager-client" );
535
+
536
+ module .exports .register = function (options , dependencies , next ) {
537
+ client .connect (options .connectionString , function (err , conn ) {
538
+ connection = conn;
539
+ next ();
540
+ });
541
+ };
542
+
543
+ module .exports .execute = function (context ) {
544
+ return function (featureName , options = {}) {
545
+ // Security: Check component permissions
546
+ const componentPermissions = getComponentPermissions (context .name );
547
+ if (! componentPermissions .canAccess (featureName)) {
548
+ throw new Error (
549
+ ` Component ${ context .name } is not authorized to access ${ featureName} `
550
+ );
551
+ }
552
+
553
+ // Audit: Log all feature access
554
+ logFeatureAccess ({
555
+ component: context .name ,
556
+ version: context .version ,
557
+ feature: featureName,
558
+ timestamp: new Date ().toISOString (),
559
+ });
560
+
561
+ // Rate limiting: Apply different limits per component
562
+ const rateLimit = getRateLimit (context .name , featureName);
563
+ if (isRateLimited (context .name , featureName, rateLimit)) {
564
+ throw new Error (
565
+ ` Rate limit exceeded for ${ context .name } accessing ${ featureName} `
566
+ );
567
+ }
568
+
569
+ // Version-specific logic
570
+ if (context .version .startsWith (" 2." )) {
571
+ // New API for v2+ components
572
+ return connection .getV2 (featureName, options);
573
+ } else {
574
+ // Legacy API for older components
575
+ return connection .getV1 (featureName);
576
+ }
577
+ };
578
+ };
579
+
580
+ module .exports .context = true ;
581
+ ```
582
+
583
+ ### Plugin Dependencies
584
+
585
+ Plugins can depend on other plugins, and context awareness works with dependencies:
586
+
587
+ ``` js
588
+ // ./registry/oc-plugins/secure-logger.js
589
+ var connection,
590
+ client = require (" ./logger-client" );
455
591
456
592
module .exports .dependencies = [" log" , " otherplugin" ];
457
593
458
594
module .exports .register = function (options , dependencies , next ) {
459
- // this register function is only called after all dependencies are registered
595
+ // This register function is only called after all dependencies are registered
460
596
client .connect (options .connectionString , function (err , conn ) {
461
597
connection = conn;
462
- dependencies .log (" hobknob client initialised " );
598
+ dependencies .log (" secure logger client initialized " );
463
599
next ();
464
600
});
465
601
};
466
602
467
- module .exports .execute = function (featureName ) {
468
- return connection .get (featureName);
603
+ module .exports .execute = function (context ) {
604
+ return function (message , level = " info" ) {
605
+ // Add component context to all log messages
606
+ const enrichedMessage = {
607
+ component: context .name ,
608
+ version: context .version ,
609
+ message: message,
610
+ level: level,
611
+ timestamp: new Date ().toISOString (),
612
+ };
613
+
614
+ return connection .log (enrichedMessage);
615
+ };
469
616
};
617
+
618
+ module .exports .context = true ;
619
+ ```
620
+
621
+ ### Plugin Configuration Options
622
+
623
+ | Property | Type | Required | Default | Description |
624
+ | ---------- | ------- | -------- | ------- | ------------------------------------------------- |
625
+ | ` name ` | string | yes | - | Unique identifier for the plugin |
626
+ | ` register ` | object | yes | - | Plugin module with register and execute functions |
627
+ | ` options ` | object | no | ` {} ` | Configuration options passed to the plugin |
628
+ | ` context ` | boolean | no | ` false ` | Enable component context awareness |
629
+
630
+ ### Context Object Structure
631
+
632
+ When ` context: true ` is set, the context object passed to the execute function contains:
633
+
634
+ ``` js
635
+ {
636
+ name: " component-name" , // The name of the component calling the plugin
637
+ version: " 1.2.3" // The version of the component calling the plugin
638
+ }
470
639
```
0 commit comments