Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/code-coverage-badge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
- name: Restore dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -53,7 +53,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand All @@ -67,4 +67,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v3
19 changes: 10 additions & 9 deletions .github/workflows/pr_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
config: 'Release'

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x

Expand Down Expand Up @@ -49,20 +49,21 @@ jobs:

# upload report as build artifact
- name: Upload a Build Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
if: ${{always()}}
with:
name: 'Test Run'
path: ${{github.workspace}}/${{env.file_name}}

# add report as PR comment (if PR)
- name: comment PR
uses: machine-learning-apps/pr-comment@master
if: ${{ github.event_name == 'pull_request' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Comment on PR
uses: peter-evans/create-or-update-comment@v4
if: github.event_name == 'pull_request'
with:
path: ${{env.file_name}}
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
body-path: ${{ env.file_name }}
edit-mode: replace

- name: Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
config: 'Release'

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x

Expand Down
65 changes: 44 additions & 21 deletions PortaCapena.OdooJsonRpcClient.Example/OdooClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@
var model = OdooModelMapper.GetDotNetModel(tableName, modelResult.Value);
}


[Fact]
public async Task Can_get_all_products_as_dictionary()
{
var odooClient = new OdooClient(TestConfig);

var products = await odooClient.GetAsync("product.product");

products.Error.Should().BeNull();
products.Value.Should().NotBeNull();
products.Value.Length.Should().BeGreaterThan(0);
products.Succeed.Should().BeTrue();
}


[Fact]
public async Task Can_get_all_products()
{
Expand Down Expand Up @@ -173,7 +188,7 @@
x.Description,
x.WriteDate
})
// .Where(x => x.Name, OdooOperator.EqualsTo, "Bioboxen 610l")
// .Where(x => x.Name, OdooOperator.EqualsTo, "Bioboxen 610l")
.Where(x => x.WriteDate, OdooOperator.GreaterThanOrEqualTo, new DateTime(2020, 12, 2));

var products = await odooClient.GetAsync<ProductProductOdooModel>(filters);
Expand All @@ -189,28 +204,17 @@

#region Create

// [Fact(Skip = "Test for working on Odoo")]
[Fact]
public async Task Can_Create_customer()
{
var name = "dupa";
var city = "dupa";
var postalCode = "dupa";
var vatEu = "test";
var address = "dupa";
var isCompany = true;

CompanyTypeResPartnerOdooEnum? test = CompanyTypeResPartnerOdooEnum.Company;

var model = OdooDictionaryModel.Create(() => new ResPartnerOdooModel()
{
Name = name,
Name = "test name",
CountryId = 20,
City = city,
Zip = postalCode,
//Vat = vatEu,
Street = address,
CompanyType = test ?? CompanyTypeResPartnerOdooEnum.Individual
City = "test city",
Zip = "12345",
Street = "test address",
CompanyType = CompanyTypeResPartnerOdooEnum.Company
});

var odooClient = new OdooClient(TestConfig);
Expand All @@ -220,6 +224,25 @@
products.Succeed.Should().BeTrue();
}

[Fact]
public async Task Can_Create_customer_with_dictionary()
{
var model = new OdooDictionaryModel("res.partner") {
{ "name", "test name" },
{ "country_id", 20 },
{ "city", "test city" },
{ "zip", "12345" },
{ "street", "test address" },
{ "company_type", "company" },
};

var odooClient = new OdooClient(TestConfig);

var products = await odooClient.CreateAsync(model);

products.Succeed.Should().BeTrue();
}

[Fact]
public async Task Can_create_update_get_and_delete_customer()
{
Expand Down Expand Up @@ -506,7 +529,7 @@
}

[Fact(Skip = "Test for working on Odoo")]
// [Fact]
// [Fact]
public async Task Can_create_purchase_order()
{
var odooClient = new OdooClient(TestConfig);
Expand All @@ -522,7 +545,7 @@
Name = "test purchase",
});

var lines = new[] {
var lines = new[] {
OdooDictionaryModel.Create(() => new PurchaseOrderLineOdooModel()
{
Name = "test purchase line",
Expand All @@ -537,8 +560,8 @@
createResult.Succeed.Should().BeTrue();
}

// [Fact(Skip = "Test for working on Odoo")]
[Fact]
// [Fact(Skip = "Test for working on Odoo")]
[Fact]
public async Task Can_create_update_and_delete_product()
{
var odooClient = new OdooClient(TestConfig);
Expand Down Expand Up @@ -588,7 +611,7 @@
{
try
{
using (var client = new WebClient())

Check warning on line 614 in PortaCapena.OdooJsonRpcClient.Example/OdooClientTests.cs

View workflow job for this annotation

GitHub Actions / build

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 614 in PortaCapena.OdooJsonRpcClient.Example/OdooClientTests.cs

View workflow job for this annotation

GitHub Actions / build

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
{
client.Credentials = new NetworkCredential(TestConfig.UserName, TestConfig.Password);
var json = "{\"jsonrpc\":\"2.0\",\"method\":\"GUI.ShowNotification\",\"params\":{\"title\":\"This is the title of the message\",\"message\":\"This is the body of the message\"},\"id\":1}";
Expand Down
5 changes: 5 additions & 0 deletions PortaCapena.OdooJsonRpcClient/Converters/OdooModelMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public static string ConvertToDotNetPropertyTypeName(KeyValuePair<string, OdooPr
case OdooValueTypeEnum.Reference:
return ConvertOdooNameToDotNet(property.Value.RelationField) + OdooModelSuffix;

case OdooValueTypeEnum.Properties:
return "string";
case OdooValueTypeEnum.PropertiesDefinition:
return "string";

default:
throw new ArgumentException($"Not expected Property Value Type: '{property.Value.PropertyValueType}'");
}
Expand Down
9 changes: 7 additions & 2 deletions PortaCapena.OdooJsonRpcClient/Models/OdooPropertyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ public static OdooValueTypeEnum ToOdooValueTypeEnum(string value)
return OdooValueTypeEnum.One2One;
case "monetary":
return OdooValueTypeEnum.Monetary;

case "properties":
return OdooValueTypeEnum.Properties;
case "properties_definition":
return OdooValueTypeEnum.PropertiesDefinition;
}
throw new Exception($"Cannot unmarshal Enum '{nameof(OdooValueTypeEnum)}' - '{value}'");
}
Expand Down Expand Up @@ -147,6 +150,8 @@ public enum OdooValueTypeEnum

Selection,
Text,
Html
Html,
PropertiesDefinition,
Properties
};
}
60 changes: 60 additions & 0 deletions PortaCapena.OdooJsonRpcClient/OdooClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static string BasicAuthenticationUsernamePassword

static OdooClient()
{
System.Net.ServicePointManager.Expect100Continue = false;
InitializeHttpClient();
}

Expand Down Expand Up @@ -110,6 +111,20 @@ public OdooClient(OdooConfig config)
var tableName = OdooExtensions.GetOdooTableName<T>();
var request = OdooRequestModel.SearchRead(odooConfig, userUid, tableName, query, context);
return await CallAndDeserializeAsync<T[]>(request, cancellationToken);
}

public async Task<OdooResult<OdooDictionaryModel[]>> GetAsync(string tableName, OdooQuery query = null, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await ExecuteWitrAccesDenideRetryAsync(userUid => GetAsync(tableName, userUid, query, SelectContext(context, Config.Context), cancellationToken));
}
public async Task<OdooResult<OdooDictionaryModel[]>> GetAsync(string tableName, int userUid, OdooQuery query = null, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await GetAsync(tableName, Config, userUid, query, SelectContext(context, Config.Context), cancellationToken);
}
public static async Task<OdooResult<OdooDictionaryModel[]>> GetAsync(string tableName, OdooConfig odooConfig, int userUid, OdooQuery query = null, OdooContext context = null, CancellationToken cancellationToken = default)
{
var request = OdooRequestModel.SearchRead(odooConfig, userUid, tableName, query, context);
return await CallAndDeserializeAsync<OdooDictionaryModel[]>(request, cancellationToken);
}

#endregion
Expand Down Expand Up @@ -154,7 +169,52 @@ public static async Task<OdooResult<long>> CreateAsync(OdooConfig odooConfig, in
var request = OdooRequestModel.Create(odooConfig, userUid, GetTableName(model), model, context);
var result = await CallAndDeserializeAsync<long>(request, cancellationToken);
return result.Succeed ? result.ToResult(result.Value) : OdooResult<long>.FailedResult(result);
}

public async Task<OdooResult<long[]>> CreateAsync(IEnumerable<IOdooCreateModel> models, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await ExecuteWitrAccesDenideRetryAsync(userUid => CreateAsync(Config, userUid, models, SelectContext(context, Config.Context), cancellationToken));
}
public static async Task<OdooResult<long[]>> CreateAsync(OdooConfig odooConfig, int userUid, IEnumerable<IOdooCreateModel> models, OdooContext context = null, CancellationToken cancellationToken = default)
{
var request = OdooRequestModel.Create(odooConfig, userUid, models.First().OdooTableName(), models, context);
var result = await CallAndDeserializeAsync<long[]>(request, cancellationToken);
return result.Succeed ? result.ToResult(result.Value) : OdooResult<long[]>.FailedResult(result);
}
public async Task<OdooResult<long[]>> CreateAsync(IEnumerable<OdooDictionaryModel> models, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await ExecuteWitrAccesDenideRetryAsync(userUid => CreateAsync(Config, userUid, models, SelectContext(context, Config.Context), cancellationToken));
}
public static async Task<OdooResult<long[]>> CreateAsync(OdooConfig odooConfig, int userUid, IEnumerable<OdooDictionaryModel> models, OdooContext context = null, CancellationToken cancellationToken = default)
{
var request = OdooRequestModel.Create(odooConfig, userUid, GetTableName(models.First()), models, context);
var result = await CallAndDeserializeAsync<long[]>(request, cancellationToken);
return result.Succeed ? result.ToResult(result.Value) : OdooResult<long[]>.FailedResult(result);
}
public async Task<OdooResult<object>> ActionAsync(string tableName, string action, object param, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await ExecuteWitrAccesDenideRetryAsync(userUid => ActionAsync(Config, userUid, tableName, action, param, SelectContext(context, Config.Context), cancellationToken));
}
public static async Task<OdooResult<object>> ActionAsync(OdooConfig odooConfig, int userUid, string tableName, string action, object model
, OdooContext context = null, CancellationToken cancellationToken = default)
{
var request = OdooRequestModel.Action(odooConfig, userUid, tableName, action, model, context);
var result = await CallAndDeserializeAsync<object>(request, cancellationToken);
return result.Succeed ? result.ToResult(result.Value) : OdooResult<object>.FailedResult(result);
}
public async Task<OdooResult<object>> ActionCollectionAsync(string tableName, string action, object param, OdooContext context = null, CancellationToken cancellationToken = default)
{
return await ExecuteWitrAccesDenideRetryAsync(userUid => ActionCollectionAsync(Config, userUid, tableName, action, param, SelectContext(context, Config.Context), cancellationToken));
}
public static async Task<OdooResult<object>> ActionCollectionAsync(OdooConfig odooConfig, int userUid, string tableName, string action, object model
, OdooContext context = null, CancellationToken cancellationToken = default)
{
var request = OdooRequestModel.ActionCollection(odooConfig, userUid, tableName, action, model, context);
var result = await CallAndDeserializeAsync<object>(request, cancellationToken);
return result.Succeed ? result.ToResult(result.Value) : OdooResult<object>.FailedResult(result);
}



#endregion

Expand Down
15 changes: 14 additions & 1 deletion PortaCapena.OdooJsonRpcClient/OdooRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using PortaCapena.OdooJsonRpcClient.Extensions;
using PortaCapena.OdooJsonRpcClient.Models;
using PortaCapena.OdooJsonRpcClient.Request;
Expand Down Expand Up @@ -28,6 +29,14 @@ public async Task<OdooResult<long>> CreateAsync(OdooDictionaryModel model, OdooC
{
return await OdooClient.CreateAsync(model, context);
}
public async Task<OdooResult<long[]>> CreateAsync(IEnumerable<IOdooCreateModel> models, OdooContext context = null)
{
return await OdooClient.CreateAsync(models, context);
}
public async Task<OdooResult<long[]>> CreateAsync(IEnumerable<OdooDictionaryModel> models, OdooContext context = null)
{
return await OdooClient.CreateAsync(models, context);
}

public async Task<OdooResult<bool>> UpdateAsync(IOdooCreateModel model, long id, OdooContext context = null)
{
Expand Down Expand Up @@ -59,5 +68,9 @@ public async Task<OdooResult<bool>> DeleteRangeAsync(T[] models, OdooContext con
{
return await OdooClient.DeleteRangeAsync(models as IOdooModel[], context);
}
public async Task<OdooResult<object>> ActionAsync(string action, object args, OdooContext context = null)
{
return await OdooClient.ActionAsync(TableName, action, args, context);
}
}
}
10 changes: 10 additions & 0 deletions PortaCapena.OdooJsonRpcClient/Request/OdooRequestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ public static OdooRequestModel Metadata(OdooConfig config, int uid, string table
var param = new OdooRequestParams(config.ApiUrlJson, "object", "execute_kw", config.DbName, uid, config.Password, tableName, OdooOperation.GetMetadata, new object[] { ids }, MapQuery(context));
return new OdooRequestModel(param);
}
public static OdooRequestModel Action(OdooConfig config, int uid, string tableName, string action, object model, OdooContext context = null)
{
var param = new OdooRequestParams(config.ApiUrlJson, "object", "execute_kw", config.DbName, uid, config.Password, tableName, action, new[] { model }, MapQuery(context));
return new OdooRequestModel(param);
}
public static OdooRequestModel ActionCollection(OdooConfig config, int uid, string tableName, string action, object model, OdooContext context = null)
{
var param = new OdooRequestParams(config.ApiUrlJson, "object", "execute_kw", config.DbName, uid, config.Password, tableName, action, model, MapQuery(context));
return new OdooRequestModel(param);
}

protected static Dictionary<string, object> MapQuery(OdooContext context, OdooQuery query = null)
{
Expand Down
Loading
Loading