Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: generate one package from few wsdl #220

Open
4n70w4 opened this issue Aug 25, 2020 · 9 comments
Open

Feature: generate one package from few wsdl #220

4n70w4 opened this issue Aug 25, 2020 · 9 comments
Assignees
Labels

Comments

@4n70w4
Copy link

4n70w4 commented Aug 25, 2020

Hi! One vendor provides its services as microservices. Approximately 20 microservices with 1-2 methods. Separate wsdl for each service.

It doesn't seem like a good idea to generate 20 packages for each service and include 20 dependencies.

I would like to be able to generate 1 package from an unlimited number of wsdl.

@4n70w4 4n70w4 changed the title Generate one package from few wsdl Feature: generate one package from few wsdl Aug 25, 2020
@mikaelcom
Copy link
Member

My point of view is that you can generate the packages in the same directory with the same main namespace then namespace each microservice under the main namespace such as:

|_ src/
|__ MyMicroServices/
|___ MicroService1/
|____ StructType/
|____ ServiceType
|_____ Call.php
|_____ Get.php
|_____ etc.
|____ ClassMap.php
|___ MicroService2/
|____ StructType/
|____ ServiceType
|_____ Set.php
|_____ Do.php
|_____ Set.php
|_____ Update.php
|_____ etc.
|____ ClassMap.php
|___ etc.
|_ vendor/
|_ composer.json

Would it match your needs?

@mikaelcom mikaelcom self-assigned this Aug 25, 2020
@4n70w4
Copy link
Author

4n70w4 commented Aug 25, 2020

In this case, I have to run the generate:package command 20 times with a different set of parameters. And also run 20 commands when updating services. This can be solved through bash scripts or ansible.

And also manually edit composer.json when adding or removing microservices. But I have no ideas how to automate it.

@mikaelcom
Copy link
Member

In this case, I have to run the generate:package command 20 times with a different set of parameters. And also run 20 commands when updating services. This can be solved through bash scripts or ansible.

And also manually edit composer.json when adding or removing microservices. But I have no ideas how to automate it.

Indeed, this can seem overaded but this is the way as it is now.

You composer.json file does not have to change for each update as only the main namespace, MyMicroServices, has to be defined within it as every package should be a sub namespace MyMicroServices\MicroService1, MyMicroServices\MicroService2, etc...

@4n70w4
Copy link
Author

4n70w4 commented Aug 31, 2020

I still do not understand with what parameters the generator should be called to get such a structure.

@4n70w4
Copy link
Author

4n70w4 commented Sep 1, 2020

My usercase. I generate packages via wsdltophp and publish them as is in my selfhosted bitbucket server.
Next I require those packages in projects via composer.

With a bunch of microservices, the problem of creating many small repositories, generating packages, publishing, and require. Too much fuss.

It would be very convenient to be able to generate one large package with several such microservices.

@mikaelcom
Copy link
Member

From my point of view, you would need to create one git project containing all the generated package under the same namespace and then require this unique dependency from your other project.

I currently do this soort of thing using a PHP script (also possible with a command line script):

$rootDir = dirname(__DIR__).'/public_html';

require_once $rootDir.'/vendor/autoload.php';

use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
use WsdlToPhp\PackageGenerator\Generator\Generator;

// define all my WSDL and their package destination
$wsdls = [
    $rootDir.'/wsdl/provider1/service1/service.wsdl' => [
        'src' => $rootDir.'/src/ProviderService/Provider1/Service1/',
        'ns' => 'ProviderService\Provider\Service1',
    ],
    $rootDir.'/wsdl/provider1/service2/service.wsdl' => [
        'src' => $rootDir.'/src/ProviderService/Provider1/Service2/',
        'ns' => 'ProviderService\Provider\Service2',
    ],
    $rootDir.'/wsdl/provider1/service3/service.wsdl' => [
        'src' => $rootDir.'/src/ProviderService/Provider1/Service3/',
        'ns' => 'ProviderService\Provider\Service3',
    ],
    $rootDir.'/wsdl/provider2/service1/service.wsdl' => [
        'src' => $rootDir.'/src/ProviderService/Provider2/Service1/',
        'ns' => 'ProviderService\Provider2\Service1',
    ],
    // etc.
];

// clean existing files, avoid having not useful files anymore
exec('rm -rf '.$rootDir.'/src/ProviderService/Provider1/Service1/');
exec('rm -rf '.$rootDir.'/src/ProviderService/Provider1/Service2/');
exec('rm -rf '.$rootDir.'/src/ProviderService/Provider1/Service3/');
exec('rm -rf '.$rootDir.'/src/ProviderService/Provider2/Service1/');

// loop over each WSDL and generate its package
foreach ($wsdls as $wsdl => $settings) {
    // Options definition: the configuration file parameter is optional
    $options = GeneratorOptions::instance();
    $options
        ->setOrigin($wsdl)
        ->setDestination($settings['src'])
        ->setNamespace($settings['ns'])
        // ->setSoapClientClass(SoapClientBase::class)
        ->setStandalone(false) // no composer.json needed
        ->setGenerateTutorialFile(false) // no tutorial.php filke needed
        ->setSrcDirname(''); // no src folder needed for the generated package
    // Generator instanciation
    $generator = new Generator($options);
    // Package generation
    $generator->generatePackage();
    fwrite(STDERR, PHP_EOL.$settings['src'].' / '.$settings['ns']);
}

Within the composer.json file, I put:

"autoload": {
        "psr-4": {
            "ProviderService\\": "src/ProviderService"
        }
    },

I then use any service from the ProviderService namespace.

@4n70w4
Copy link
Author

4n70w4 commented Sep 2, 2020

@mikaelcom thank! This way looks good.

@gugglegum
Copy link
Contributor

gugglegum commented Nov 3, 2023

I made my own fully automated solution to generate the final composer package, ready to commit, without any manual corrections. Hope this will help to someone:

<?php

require __DIR__ . '/vendor/autoload.php';

use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
use WsdlToPhp\PackageGenerator\Generator\Generator;

$destination = __DIR__ . '/../your-company-wsdl';  // replace it with the directory of generated package
$namespace = 'Your\Company\Wsdl';         // replace it with your namespace
$package = 'username/your-company-wsdl';  // replace it with the Composer package name
$services = [ // Replace it with your list of service names and WSDL URLs
    'Inventory2' => 'https://********.com/wsdl/inventory2.0.0RC4/InventoryService.wsdl',
    'Invoice' => 'https://********.com/wsdl/InvoiceService.wsdl',
    'PurchaseOrder' => 'https://********.com/wsdl/POService.wsdl',
    'OrderShipmentNotification' => 'https://********.com/wsdl/OrderShipmentNotificationService.wsdl',
    'OrderStatus' => 'https://********.com/wsdl/OrderStatusService.wsdl',
    'PricingAndConfiguration' => 'https://********.com/wsdl/PricingAndConfiguration.wsdl',
    'ProductData' => 'https://********.com/wsdl/ProductDataService.wsdl',
];

$options = GeneratorOptions::instance();
$options->setDestination($destination)
    ->setOptionValue(GeneratorOptions::COMPOSER_SETTINGS, [
        'description' => 'Your Company WSDL Client',
        'require' => [ // replace it with PHP version you need or add some dependencies if your package contains manually created classes
            'php' => '>=8.0',
        ],
    ])
    ->setNamespaceDictatesDirectories(false)
    ->setComposerName($package)
    ->setGenerateTutorialFile(false);

$composer = [];
foreach ($services as $serviceName => $wsdlUrl) {
    echo "\nGenerate {$serviceName}\n\n";
    $options->setOrigin($wsdlUrl)
        ->setSrcDirname("src/{$serviceName}")
        ->setNamespace($namespace . '\\' . $serviceName);
    $generator = new Generator($options);
    $generator->generatePackage();
    $composer = array_replace_recursive($composer, json_decode(file_get_contents($destination . '/composer.json'), true));
}

echo "\nFixing autoload in composer.json\n\n";
$composer['autoload']['psr-4'] = [
    $namespace . '\\' => "src/",
];
file_put_contents($destination . '/composer.json', json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n");

echo "Well done!\n";

It will produce the structure like:

src
  Inventory2
    ...
  Invoice
    ...
  OrderShipmentNotification
    ...
  OrderStatus
    ...
  PricingAndConfiguration
    ...
  ProductData
    ...
  PurchaseOrder
    ...
vendor
composer.json
composer.lock

The composer.json file will look like:

{
    "name": "username/your-company-wsdl",
    "description": "Your Company WSDL Client",
    "require": {
        "php": ">=8.0",
        "ext-dom": "*",
        "ext-mbstring": "*",
        "ext-soap": "*",
        "wsdltophp/packagebase": "~5.0"
    },
    "autoload": {
        "psr-4": {
            "Your\\Company\\Wsdl\\": "src/"
        }
    }
}

Please tell me what do you think about my solution. I'm looking forward to comments.

@gugglegum
Copy link
Contributor

gugglegum commented Nov 11, 2023

I reworked my previous solution for automatic generation of the Composer package with a multiple WSDL services. It works exactly the same and makes the same folder and namespace structure. But now the generation script is a part of this package. It makes easier to regenerate the code if something changed in the future, because no need to search the script that generated this package maybe several years ago.

You start creating Composer package with running this command in an empty folder of new Composer package:

composer require --dev wsdltophp/packagegenerator

It will make a simplest composer.json and install necessary vendor packages. Then place this PHP script in the root of this Composer package and execute:

<?php

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Console\Input\ArrayInput;
use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
use WsdlToPhp\PackageGenerator\Generator\Generator;

$destination = __DIR__;
$packageName = 'username/your-company-wsdl';
$packageDescription = 'Your Company WSDL Client';
$namespace = 'Your\Company\Wsdl';

// List of WSDL services in result Composer package [ 'Source-SubFolder' => 'WSDL-URL', ... ]
$services = [
    'Inventory2' => 'https://********.com/wsdl/inventory2.0.0RC4/InventoryService.wsdl',
    'Invoice' => 'https://********.com/wsdl/InvoiceService.wsdl',
    'PurchaseOrder' => 'https://********.com/wsdl/POService.wsdl',
    'OrderShipmentNotification' => 'https://********.com/wsdl/OrderShipmentNotificationService.wsdl',
    'OrderStatus' => 'https://********.com/wsdl/OrderStatusService.wsdl',
    'PricingAndConfiguration' => 'https://********.com/wsdl/PricingAndConfiguration.wsdl',
    'ProductData' => 'https://********.com/wsdl/ProductDataService.wsdl',
];

$options = (GeneratorOptions::instance())
    ->setDestination($destination)
    ->setNamespaceDictatesDirectories(false)
    ->setGenerateTutorialFile(false)
    ->setStandalone(false);

foreach ($services as $serviceName => $wsdlUrl) {
    echo "Generate {$serviceName}\n";
    $options->setOrigin($wsdlUrl)
        ->setSrcDirname("src/{$serviceName}")
        ->setNamespace($namespace . '\\' . $serviceName);
    $generator = new Generator($options);
    $generator->generatePackage();
}

echo "Write composer.json\n";
$composer = [
    'name' => $packageName,
    'description' => $packageDescription,
    'require' => [ // replace it with PHP version you need or add some dependencies if your package contains manually created classes
        'php' => '>=8.0',
        'ext-dom' => '*',
        'ext-mbstring' => '*',
        'ext-soap' => '*',
        'ext-ctype' => '*',
        'wsdltophp/packagebase' => '~5.0',
    ],
    'require-dev' => [
        'wsdltophp/packagegenerator' => '^4.1',
    ],
    'autoload' => [
        'psr-4' => [
            $namespace . '\\' => 'src/',
        ],
    ],
];
file_put_contents($destination . '/composer.json', json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");

echo "Execute \"composer update\"\n";
$composer = new \Composer\Console\Application();
$composer->setAutoExit(false);
$composer->run(new ArrayInput([
    'command' => 'update',
    '--optimize-autoloader' => true,
    '--working-dir' => $destination,
]));

echo "Well done!\n";

Now your package is ready to commit and publish. Later you can run this script again to make sure all is up-to-date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants