Skip to content

Commit

Permalink
Merge pull request #3 from comsave/wsdlvalidator
Browse files Browse the repository at this point in the history
retrieved new files
  • Loading branch information
lucacermenati authored Mar 31, 2020
2 parents eb04a9a + bd8ca8e commit e1b8a13
Show file tree
Hide file tree
Showing 9 changed files with 597 additions and 0 deletions.
159 changes: 159 additions & 0 deletions src/LogicItLab/Salesforce/MapperBundle/WsdlValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

namespace LogicItLab\Salesforce\MapperBundle;

use LogicItLab\Salesforce\MapperBundle\Annotation\AnnotationReader;
use Symfony\Component\Finder\Finder;

class WsdlValidator
{
/** @var AnnotationReader */
private $annotationReader;

/** @var string|string[] */
private $modelDirPath;

/** @var string */
private $wsdlPath;

/** @var string */
private $wsdlContents;

/**
* @param AnnotationReader $annotationReader
* @codeCoverageIgnore
*/
public function __construct(AnnotationReader $annotationReader)
{
$this->annotationReader = $annotationReader;
}

/**
* @param string|string[] $modelDirPath
* @param string $wsdlPath
* @return array
*/
public function validate($modelDirPath, string $wsdlPath): array
{
$this->modelDirPath = $modelDirPath;
$this->wsdlPath = $wsdlPath;
$missingFields = [];

foreach ($this->getAllClassAnnotations() as $annotation) {
$objectName = $annotation['object']->name;
$fieldNames = $this->getFieldNames($annotation['fields']);

if (!$objectName || !$fieldNames) {
continue;
}

foreach ($fieldNames as $fieldName) {
if ($this->hasField($objectName, $fieldName) === false) {
$missingFields[$objectName][] = $fieldName;
}
}
}

return $missingFields;
}

private function getObject(string $objectName): ?string
{
preg_match("/<complexType name=\"$objectName\">([\w\W]*)<\/complexType>/", $this->getWsdlContents(), $matches);

return $matches[0] ?? null;
}

private function hasField(string $objectName, string $fieldName): bool
{
return strpos($this->getObject($objectName), sprintf('<element name="%s"', $fieldName)) !== false;
}

private function getWsdlContents(): string
{
if (!$this->wsdlContents) {
$this->wsdlContents = file_get_contents($this->wsdlPath);
}

return $this->wsdlContents;
}

private function getAllClassAnnotations(): array
{
return array_map(function ($className) {
return $this->annotationReader->getSalesforceProperties($className);
}, $this->getAllClassNames());
}

private function getAllClassNames(): array
{
$classNames = [];
$allFiles = Finder::create()->files()->in($this->modelDirPath)->name('*.php');
foreach ($allFiles as $file) {
$namespace = $this->getNamespaceFromFile($file);
$className = $this->getClassNameFromFile($file);

$classNames[] = "$namespace\\$className";
}

return $this->filterNonExistentClasses($classNames);
}

private function getNamespaceFromFile(\SplFileInfo $file): ?string
{
$fileText = file_get_contents($file->getRealPath());

if (preg_match('#^namespace\s+(.+?);$#sm', $fileText, $matches)) {
return str_replace('/', '\\', $matches[1]);
}

return null;
}

private function getClassNameFromFile(\SplFileInfo $file): string
{
return str_replace('.php', '', $file->getFileName());
}

private function filterNonExistentClasses(array $classNames): array
{
$classNames = array_filter($classNames, function ($className) {
return class_exists($className);
});

return $classNames;
}

private function getFieldNames(?array $mapFieldAnnotation): ?array
{
if (!$mapFieldAnnotation) {
return null;
}

$fieldNames = [];
foreach ($mapFieldAnnotation as $propertyName => $annotation) {
$fieldNames[] = $annotation->name;
}

return $fieldNames;
}

private function sortByObjectName(array $missingFields): array
{
ksort($missingFields);

return $missingFields;
}

public function buildErrorMessage(array $missingFields): string
{
$list = "These objects or fields are missing in wsdl:";
foreach ($this->sortByObjectName($missingFields) as $objectName => $fields) {
foreach ($fields as $field) {
$list .= "\n$objectName -> $field";
}
}

return $list;
}
}
30 changes: 30 additions & 0 deletions src/LogicItLab/Salesforce/MapperBundle/WsdlValidatorTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace LogicItLab\Salesforce\MapperBundle;

use LogicItLab\Salesforce\MapperBundle\Annotation\AnnotationReader;
use PHPUnit\Framework\TestCase;

abstract class WsdlValidatorTestCase extends TestCase
{
public abstract function modelAndWsdlDataProvider(): array;

/**
* @param $modelsDir
* @param $wsdlPath
* @dataProvider modelAndWsdlDataProvider
*/
public function testWsdlIsValid($modelsDir, $wsdlPath)
{
$missingFields = $this->buildValidator()->validate($modelsDir, $wsdlPath);

$this->assertEmpty($missingFields, $this->buildValidator()->buildErrorMessage($missingFields));
}

private function buildValidator(): WsdlValidator
{
$annotationReader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());

return new WsdlValidator($annotationReader);
}
}
117 changes: 117 additions & 0 deletions tests/LogicItLab/Salesforce/MapperBundle/Resources/test.full.wsdl.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Salesforce.com Enterprise Web Services API Version 48.0
Generated on 2020-03-04 12:39:40 +0000.
Package Versions:
Rollup Helper (Version: 13.2, Namespace: rh2)
DashboardPal (Version: 1.1, Namespace: dashboard_pal)
Salesforce and Chatter Apps (Version: 1.19, Namespace: sf_chttr_apps)
Salesforce Connected Apps (Version: 1.7, Namespace: sf_com_apps)
Mailchimp Integration App (Version: 1.73, Namespace: mc4sf)
Desk.com: All-in-one Customer Support System (Version: 2.8, Namespace: deskcom)
Salesforce Communities Management (for Communities with Chatter) (Version: 5.1, Namespace: ca_collab_2_0)
DocuSign eSignature for Salesforce (Version: 6.3, Namespace: dsfs)
Copyright 1999-2020 salesforce.com, inc.
All Rights Reserved
-->

<definitions targetNamespace="urn:enterprise.soap.sforce.com"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="urn:enterprise.soap.sforce.com"
xmlns:fns="urn:fault.enterprise.soap.sforce.com"
xmlns:ens="urn:sobject.enterprise.soap.sforce.com">
<types>

<schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:sobject.enterprise.soap.sforce.com">

<import namespace="urn:enterprise.soap.sforce.com"/>

<!-- Base sObject (abstract) -->
<complexType name="sObject">
<sequence>
<element name="fieldsToNull" type="xsd:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
<element name="Id" type="tns:ID" nillable="true" />
</sequence>
</complexType>

<complexType name="AIRecordInsight">
<complexContent>
<extension base="ens:sObject">
<sequence>
<element name="AIInsightActions" nillable="true" minOccurs="0" type="tns:QueryResult"/>
<element name="AIInsightFeedbacks" nillable="true" minOccurs="0" type="tns:QueryResult"/>
<element name="AIInsightValues" nillable="true" minOccurs="0" type="tns:QueryResult"/>
<element name="Confidence" nillable="true" minOccurs="0" type="xsd:double"/>
<element name="CreatedBy" nillable="true" minOccurs="0" type="ens:User"/>
<element name="CreatedById" nillable="true" minOccurs="0" type="tns:ID"/>
<element name="CreatedDate" nillable="true" minOccurs="0" type="xsd:dateTime"/>
<element name="IsDeleted" nillable="true" minOccurs="0" type="xsd:boolean"/>
<element name="LastModifiedBy" nillable="true" minOccurs="0" type="ens:User"/>
<element name="LastModifiedById" nillable="true" minOccurs="0" type="tns:ID"/>
<element name="LastModifiedDate" nillable="true" minOccurs="0" type="xsd:dateTime"/>
<element name="Name" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="RunGuid" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="RunStartTime" nillable="true" minOccurs="0" type="xsd:dateTime"/>
<element name="Status" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="SystemModstamp" nillable="true" minOccurs="0" type="xsd:dateTime"/>
<element name="Target" nillable="true" minOccurs="0" type="ens:Contract"/>
<element name="TargetField" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="TargetId" nillable="true" minOccurs="0" type="tns:ID"/>
<element name="TargetSobjectType" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="Type" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="UserRecordAccess" nillable="true" minOccurs="0" type="ens:UserRecordAccess"/>
<element name="ValidUntil" nillable="true" minOccurs="0" type="xsd:dateTime"/>
</sequence>
</extension>
</complexContent>
</complexType>

<complexType name="Account">
<complexContent>
<extension base="ens:sObject">
<sequence>
<element name="AccountNumber" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="BillingCity" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="BillingCountry" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="Owner" nillable="true" minOccurs="0" type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>


<complexType name="Contact">
<complexContent>
<extension base="ens:sObject">
<sequence>
<element name="AccountId" nillable="true" minOccurs="0" type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>


<complexType name="User">
<complexContent>
<extension base="ens:sObject">
<sequence>
<element name="City" nillable="true" minOccurs="0" type="xsd:string"/>
<element name="Country" nillable="true" minOccurs="0" type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>

</types>
<!-- Soap Service Endpoint -->
<service name="SforceService">
<documentation>Sforce SOAP API</documentation>
<port binding="tns:SoapBinding" name="Soap">
<soap:address location="https://login.salesforce.com/services/Soap/c/48.0/0DF2p000000cI92"/>
</port>
</service>
</definitions>
Loading

0 comments on commit e1b8a13

Please sign in to comment.