diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a1236ce --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,50 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Build & Publish + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 18 + uses: actions/setup-java@v2 + with: + java-version: '18' + distribution: 'temurin' + cache: gradle + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Build with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: -Pversion=${{ github.event.release.tag_name || '1.0-SNAPSHOT' }} build + - name: Run Tests + uses: gradle/gradle-build-action@v2 + with: + arguments: test integrationTest + - name: Archive test report + uses: actions/upload-artifact@v2 + with: + name: Test report + path: build/reports/tests/test + - name: Publish with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: -Pversion=${{ github.event.release.tag_name || '1.0-SNAPSHOT' }} publish + env: + ACTION_DEPLOY_USER: ${{ secrets.ACTION_DEPLOY_USER }} + ACTION_DEPLOY_TOKEN: ${{ secrets.ACTION_DEPLOY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml deleted file mode 100644 index c4c4868..0000000 --- a/.github/workflows/gradle.yml +++ /dev/null @@ -1,33 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle - -name: Java CI with Gradle - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'temurin' - - name: Make gradlew executable - run: chmod +x ./gradlew - - name: Build with Gradle - uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7 - with: - arguments: build diff --git a/.gitignore b/.gitignore index cff8a2c..c79ab46 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ src/itest/resources/credentials.properties src/itest/resources/credentials.properties out/ *.iml +bin/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1848f57..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: java -jdk: -- openjdk11 -before_install: -- chmod +x ./gradlew -script: -- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./gradlew check; fi' -- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./gradlew test integrationTest sendCoverageToCodacy; fi' -deploy: -- provider: releases - api_key: - secure: ltLzm1VWcBj5sLTqtEUQyd3JG5pYZ6nQG5czsWB28hvJ3wkVa6vt7L5j3OS8wZuaIdE6eDJ2rMfQ5qS1FAE91SiQfCsMArsZ5C2cWm7BSz70MAa2zCWgxxlMsvARTnSLaP9mVgbZN0js2p0ETVVuUj+MG1dQJGXhsggmFI3ZSxCnWpoAhtGDVvQr8BZp4w5hPJ0Xm144v9ZnT2oUwmwevlVFzO+QzjiUJwPCjy4y6rJAJDdYdY0mTUQq7Y9SwkjXQACNwDRs6ITQ4nbClCk7RtDIKUFYWvuq9IRD0OhVXibTtMC4yocl+PsM6XvCYCAWPjLon20Pc2fdepzWhYPkyoOf9SDDUFAHEjYQ+vw2BkQpx3WwMZDLJbGXI1z0+evYUhSxr7oRSzoRfpCYFtEJ7toLb8SPTsYkyQQp5N1EDNtnIgBZhsQpFdxDgZ8vrVeWiLVtzX5ARgz0GV/yL2v+2ZzPg7usEJSNC6xwyjGiPGDtggUy2HIsP1ohl56VVvU5RjfoKcXxysbm0L43sddZHIuNggyku7pNlYsgHdrUf/0qanxxrU46w8M4vUdyd1oC/pXwaUkpdjA6nFU+ouoX1ScihxZzlkE2TYEFJqbKcWQ8d9zeYW90pemc8oWZJcylyF0ef2MJKy3DgNplnoSJM6q6sDunAA5wGN5A+vu/Ahw= - skip_cleanup: true - file_glob: true - file: "./build/libs/*" - on: - repo: Sybit-Education/airtable.java - tags: true -- provider: script - skip_cleanup: true - script: "./gradlew -i bintrayUpload" - on: - repo: Sybit-Education/airtable.java - tags: true -before_cache: -- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock -cache: - directories: "— $HOME/.gradle" diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/README.md b/README.md index b05c2a9..0ef57ae 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ -[![Build Status](https://travis-ci.org/Sybit-Education/airtable.java.svg?branch=master)](https://travis-ci.org/Sybit-Education/airtable.java) +# Airtable.java - The Java API for Airtable. + [![Codacy Badge](https://api.codacy.com/project/badge/Grade/25c71982881d40eeb1517e65827f5c62)](https://www.codacy.com/app/stritti/airtable-java?utm_source=github.com&utm_medium=referral&utm_content=Sybit-Education/airtable.java&utm_campaign=badger) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/25c71982881d40eeb1517e65827f5c62)](https://www.codacy.com/app/Sybit-Education/airtable-java?utm_source=github.com&utm_medium=referral&utm_content=Sybit-Education/airtable.java&utm_campaign=Badge_Coverage) -[![Download](https://api.bintray.com/packages/sybit-education/maven/airtable.java/images/download.svg) ](https://bintray.com/sybit-education/maven/airtable.java/_latestVersion) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) - -# Airtable.java - -This is a Java API client for Airtable (http://www.airtable.com). +This is a Java API client for Airtable (). The Airtable API provides a simple way of accessing data within Java projects. -More information about the Airtable API could be found at [https://airtable.com/api](https://airtable.com/api). +More information about the Airtable API could be found at [https://airtable.com/api](https://airtable.com/api). The documentation will provide detailed information about your created base. # Usage @@ -22,12 +19,16 @@ For adding dependency, you could use bintray-repository: The files are stored at: [https://dl.bintray.com/sybit-education/maven/](https://dl.bintray.com/sybit-education/maven/) ## Gradle + For Gradle add compile `com.sybit:airtable.java:[version]` to compile dependencies. -Also add `jcenter` repository to dependencies: +Also add repository to dependencies: + ``` repositories { - jcenter() - ... + maven { + url = uri("https://maven.pkg.github.com/Sybit-Education/airtable.java") + ... + } } ``` @@ -42,16 +43,20 @@ Airtable airtable = new Airtable().configure(); ``` ### API-Key -The API key could be passed to the app in different ways: + +The API key could be passed to the app in different ways: + * Defining Java property `AIRTABLE_API_KEY` (e.g. `-DAIRTABLE_API_KEY=foo`). * Defining OS environment variable `AIRTABLE_API_KEY` (e.g. `export AIRTABLE_API_KEY=foo`). * Defining property file `credentials.properties` in root classpath containing key/value `AIRTABLE_API_KEY=foo`. * On the other hand the API-key could also be added by using the method `Airtable.configure(String apiKey)`. #### How to get API-Key -See: https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key- + +See: - ### Proxy Support + The API supports environment variable `http_proxy`. If the variable is set, it is used automatically. * On Windows: `set http_proxy=http://your_proxy:your_port` @@ -61,15 +66,17 @@ If `endpointUrl` contains `localhost` or `127.0.0.1` proxy settings are ignored ### Logging -The Simple Logging Facade for Java [SLF4J](https://www.slf4j.org/) serves as a simple facade or abstraction -for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired +The Simple Logging Facade for Java [SLF4J](https://www.slf4j.org/) serves as a simple facade or abstraction +for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time. ### Request Limits -The API of Airtable itself is limited to 5 requests per second. If you exceed this rate, you will receive a 429 status code and will + +The API of Airtable itself is limited to 5 requests per second. If you exceed this rate, you will receive a 429 status code and will need to wait 30 seconds before subsequent requests will succeed. ### Connecting to Airtable + To use this libraray you will need an Airtable object. Simply create one: `Airtable airtable = new Airtable();`. This object needs an API-Key or it won't work properly so `airtable.configure(AIRTABLE_API_KEY);`. Now the Airtable object needs to know which base you want to access. This method will return a Base object which will be used in the future: @@ -77,12 +84,13 @@ Now the Airtable object needs to know which base you want to access. This method With the Base object you can perform all kind of operations see more at [CRUD Operations on Table Records](#crud-operations-on-table-records). - ## Object Mapping + The Java implementation of the Airtable API provides automatic object mapping. You can map any table to your own Java classes. But first you need to specify those classes. ### Create a Object + The Java objects represent records or 'values' in Airtable. So the class attributes need to be adjusted to the Airtable Base. #### Example @@ -99,6 +107,7 @@ This is how our 'Actor' table looks like: | ... | ... | ... | ... | ... | Now our Java class should look like this: + ```Java public class Actor { @@ -153,11 +162,11 @@ Now our Java class should look like this: } } ``` -For each column we give the Java class an attribute with the column name (Be careful! See more about naming in the Section [Annotations](#annotations)) + +For each column we give the Java class an attribute with the column name (Be careful! See more about naming in the Section [Annotations](#annotations)) and add Getters and Setters for each attribute. The attribute types can be either primitive Java types like `String` and `Float` for Text and Numbers, `String Array` for references on other Tables or `Attachment` for attached photos and files. - Now we got everything we need to create our first Airtable table object. We use the Java class we just wrote to specify what kind of Object should be saved in our table. Then we tell our `base`-object which table we want to access. All the records saved in our Airtable Base now should be in our local Table Object. @@ -173,10 +182,12 @@ Example: ``` ### Basic Objects + The Java implementation of the Airtable-API provides an implementation of basic Airtable objects such as attachments and thumbnails. Photos and attached files in Airtable are retrieved as `Attachment`s. Photos furthermore contain `Thumbnail`-Objects for different sizes. #### Attachment + All the `Attachment`-objects got the following attributes: * String `id` @@ -190,6 +201,7 @@ Photos additionally have: * Map `thumbnails` #### Thumbnails + A Thumbnail is generated for image files in Airtable. Thumbnails are bound to an `Attachment`-object as a key/value Map. The keys are `small` and `large` for the different sizes. The value is a `Thumbnail`-object. @@ -208,6 +220,7 @@ Use the annotation `@SerializedName` of [Gson](https://github.com/google/gson) t The airtable.java API will respect these mappings automatically. #### Example + ```Java import com.google.gson.annotations.SerializedName; @@ -216,8 +229,10 @@ The airtable.java API will respect these mappings automatically. @SerializedName("First- & Lastname") private String name; ``` + ### Sort -With the integrated `Sort` element you can retrieve a list of sorted objects that specifies how the records will be ordered. + +With the integrated `Sort` element you can retrieve a list of sorted objects that specifies how the records will be ordered. Each sort object must have a field key specifying the name of the field to sort on, and an optional direction key that is either "asc" or "desc". The default direction is "asc". @@ -227,25 +242,26 @@ For example, to sort records by Name, pass in: Sort sort = new Sort("Name", Sort.Direction.desc); List listMovies = movieTable.select(sort); ``` + If you set the view parameter, the returned records in that view will be sorted by these fields. Detailed example see [TableParameterTest](https://github.com/Sybit-Education/airtable.java/blob/develop/src/itest/java/com/sybit/airtable/TableParameterTest.java) - ## CRUD-Operations on Table Records ## Select -Select list of items from table: -+ `table(name).select()`: get all records of table `name` -+ `table(name).select(Integer maxRecords)`: get max `maxRecords` records of table `name` -+ `table(name).select(String[] fields)`: get records of table `name` with only the specified `fields` -+ `table(name).select(String view)`: get records of table `name` with the specified `view` (more about [views](https://support.airtable.com/hc/en-us/sections/200644955-Views)) -+ `table(name).select(Sort sortation)`: get records of table `name` using `sort` to sort records (More about Sort [here](#sort)) -+ `table(name).select(Query query)`: get records of table `name` using `query` to filter +Select list of items from table: +* `table(name).select()`: get all records of table `name` +* `table(name).select(Integer maxRecords)`: get max `maxRecords` records of table `name` +* `table(name).select(String[] fields)`: get records of table `name` with only the specified `fields` +* `table(name).select(String view)`: get records of table `name` with the specified `view` (more about [views](https://support.airtable.com/hc/en-us/sections/200644955-Views)) +* `table(name).select(Sort sortation)`: get records of table `name` using `sort` to sort records (More about Sort [here](#sort)) +* `table(name).select(Query query)`: get records of table `name` using `query` to filter ### Example + ```Java Base base = new Airtable().base("AIRTABLE_BASE"); List retval = base.table("Movies", Movie.class).select(); @@ -254,15 +270,18 @@ List retval = base.table("Movies", Movie.class).select(); Detailed example see [TableSelectTest.java](https://github.com/Sybit-Education/airtable.java/blob/develop/src/itest/java/com/sybit/airtable/TableSelectTest.java) ### API Result Limitation + The REST-API of Airtable is limited to return max. 100 records. If the select has more than 100 records in result an `offest` is added to -returned data. The Airtable.java client will solve this and tries to load the offset data automatically. +returned data. The Airtable.java client will solve this and tries to load the offset data automatically. ## Find + Use `find` to get specific records of table: -+ `table(name).find(String id)`: get record with `id` of table `name` +* `table(name).find(String id)`: get record with `id` of table `name` ### Example + ```Java Base base = new Airtable().base("AIRTABLE_BASE"); Table actorTable = base.table("Actors", Actor.class); @@ -272,28 +291,33 @@ Actor actor = actorTable.find("rec514228ed76ced1"); Detailed example see [TableFindTest.java](https://github.com/Sybit-Education/airtable.java/blob/develop/src/itest/java/com/sybit/airtable/TableFindTest.java) ## Destroy + Use `destroy` to delete a specific records of table: -+ `table(name).destroy(String id)`: delete record with `id` of table `name` +* `table(name).destroy(String id)`: delete record with `id` of table `name` ### Example + ```Java Base base = airtable.base("AIRTABLE_BASE"); Table actorTable = base.table("Actors", Actor.class); actorTable.destroy("recapJ3Js8AEwt0Bf"); ``` + Detailed example see [TableDestroyTest.java](https://github.com/Sybit-Education/airtable.java/blob/develop/src/itest/java/com/sybit/airtable/TableDestroyTest.java) ## Create + First build your record. Then use `create` to generate a specific records of table: -+ `Table actorTable = base.table("Actors", Actor.class); +* `Table actorTable = base.table("Actors", Actor.class); Actor newActor = new Actor(); newActor.setName("Neuer Actor");`: build your record `Actor test = actorTable.create(newActor);`: create the recently build record ### Example + ```Java // detailed Example see TableCreateTest.java Base base = airtable.base("AIRTABLE_BASE"); @@ -308,13 +332,15 @@ Actor test = actorTable.create(newActor); Detailed example see [TableCreateRecordTest.java](https://github.com/Sybit-Education/airtable.java/blob/develop/src/itest/java/com/sybit/airtable/TableCreateRecordTest.java) ## Update + Use `update` to update a record of table: -+ `Actor.setName("New Name");`: set or update to a new Value +* `Actor.setName("New Name");`: set or update to a new Value `Actor test = actorTable.update(Actor);`: update the Actor in the Table ### Example + ```Java // detailed Example see TableCreateTest.java @@ -330,29 +356,29 @@ Detailed example see [TableUpdateTest](https://github.com/Sybit-Education/airtab Short overview of features, which are supported: -+ [x] Airtable Configure - + [x] configuration of `proxy` - + [x] configuration of `AIRTABLE_API_KEY` & `AIRTABLE_BASE` - + [x] configuration of `requestTimeout` - -+ [x] Select Records - + [x] SelectAll - + [x] Queries (`maxRecords`, `sort` & `view` ) - + [x] Support of `filterByFormula` - + [x] Support of `paging` - + [x] Support of appending `offset` data - -+ [x] Find Record -+ [x] Create Record -+ [x] Update Record -+ [ ] Replace Record (could be done by update) -+ [x] Delete/Destroy Record -+ General requirements - + [x] Automatic ObjectMapping - + [x] Read: convert to Objects - + [x] Read: conversion of `Attachment`s & `Thumbnail`s - + [x] Write: convert Objects to JSON -+ [x] Errorhandling +* [x] Airtable Configure + * [x] configuration of `proxy` + * [x] configuration of `AIRTABLE_API_KEY` & `AIRTABLE_BASE` + * [x] configuration of `requestTimeout` + +* [x] Select Records + * [x] SelectAll + * [x] Queries (`maxRecords`, `sort` & `view` ) + * [x] Support of `filterByFormula` + * [x] Support of `paging` + * [x] Support of appending `offset` data + +* [x] Find Record +* [x] Create Record +* [x] Update Record +* [ ] Replace Record (could be done by update) +* [x] Delete/Destroy Record +* General requirements + * [x] Automatic ObjectMapping + * [x] Read: convert to Objects + * [x] Read: conversion of `Attachment`s & `Thumbnail`s + * [x] Write: convert Objects to JSON +* [x] Errorhandling # Contribute @@ -362,15 +388,15 @@ see: [CONTRIBUTING.md](./CONTRIBUTING.md) There are JUnit tests and integration tests to verify the API. The integration tests are based on the Airtable template [Movies](https://airtable.com/templates/groups-clubs-and-hobbies/exprTnrH3YV8Vv9BI/favorite-movies) which could be created in your account. -For testing, the JSON-responses are mocked by [WireMock](http://wiremock.org/). +For testing, the JSON-responses are mocked by [WireMock](http://wiremock.org/). # Other Airtable Projects -- [Airtable.js](https://github.com/Airtable/airtable.js): JavaScript Client -- [Airtable Ruby Client](https://github.com/Airtable/airtable-ruby): Ruzby Client -- [Airtable Phyton](https://github.com/nicocanali/airtable-python): Phyton Client -- [Airtabler](https://github.com/bergant/airtabler): R interface to the Airtable API -- [Airtable.cs](https://github.com/alphamax/AirTable.cs): AirTable API .Net client. +* [Airtable.js](https://github.com/Airtable/airtable.js): JavaScript Client +* [Airtable Ruby Client](https://github.com/Airtable/airtable-ruby): Ruzby Client +* [Airtable Phyton](https://github.com/nicocanali/airtable-python): Phyton Client +* [Airtabler](https://github.com/bergant/airtabler): R interface to the Airtable API +* [Airtable.cs](https://github.com/alphamax/AirTable.cs): AirTable API .Net client. More Github-Projects using topic *[Airtable](https://github.com/search?q=topic%3Aairtable&type=Repositories)* @@ -380,13 +406,13 @@ Thank you very much for these great frameworks and libraries provided open sourc We use following libraries: -+ [unirest](http://unirest.io/java.html) -+ [Google gson](https://github.com/google/gson) -+ [Apache Commons Beanutils](http://commons.apache.org/proper/commons-beanutils/) -+ [Apache Commons IO](http://commons.apache.org/proper/commons-io/) -+ [slf4j](https://www.slf4j.org) -+ [JUnit](http://junit.org) -+ [WireMock](http://wiremock.org/) +* [unirest](http://unirest.io/java.html) +* [Google gson](https://github.com/google/gson) +* [Apache Commons Beanutils](http://commons.apache.org/proper/commons-beanutils/) +* [Apache Commons IO](http://commons.apache.org/proper/commons-io/) +* [slf4j](https://www.slf4j.org) +* [JUnit](http://junit.org) +* [WireMock](http://wiremock.org/) # License diff --git a/build.gradle b/build.gradle index db8de21..05e3e30 100644 --- a/build.gradle +++ b/build.gradle @@ -5,9 +5,15 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* - * Gets the version name from the latest Git tag - */ + +plugins { + id "maven-publish" + id "jacoco" + id 'com.github.jk1.dependency-license-report' version '2.0' +} + +apply plugin: 'java' + def getVersionName = { -> try { def stdout = new ByteArrayOutputStream() @@ -18,43 +24,22 @@ def getVersionName = { -> return stdout.toString().trim() } catch (ignored) { - return version; + return ""; } -} +} as Object -buildscript { - repositories { - mavenLocal() - jcenter() - } - - dependencies { - classpath 'com.palantir:jacoco-coverage:0.4.0' - classpath "com.smokejumperit.gradle.license:Gradle-License-Report:0.0.2" - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' - } -} +group = 'com.sybit.airtable' +//version = getVersionName() +description = """API to access Airtable""" -allprojects { - repositories { - jcenter() - } - apply plugin: 'java' - apply plugin: 'maven' - apply plugin: 'maven-publish' - apply plugin: 'com.palantir.jacoco-coverage' - apply plugin: 'com.jfrog.bintray' +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + sourceCompatibility = 1.8 + targetCompatibility = 1.8 } -group = 'com.sybit' -version = getVersionName() - -description = """com.sybit airtable""" - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' +tasks.named("processTestResources") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } sourceSets { @@ -65,22 +50,20 @@ sourceSets { //runtimeClasspath += files('src/itest/resources/com/buransky') // Use "java" if you don't use Scala as a programming language java.srcDir file('src/itest/java') - } - // This is just to trick IntelliJ IDEA to add integration test - // resources to classpath when running integration tests from - // the IDE. It's is not a good solution but I don't know about - // a better one. - test { - resources.srcDir file('src/itest/resources') - } + } + // This is just to trick IntelliJ IDEA to add integration test + // resources to classpath when running integration tests from + // the IDE. It's is not a good solution but I don't know about + // a better one. + test { + resources.srcDir file('src/itest/resources') + } } repositories { - mavenLocal() - jcenter() mavenCentral() maven { url "https://jitpack.io" } - maven { url "http://dl.bintray.com/typesafe/maven-releases" } + maven { url "https://dl.bintray.com/typesafe/maven-releases" } } configurations { @@ -90,21 +73,26 @@ configurations { } dependencies { - compile group: 'com.mashape.unirest', name: 'unirest-java', version:'1.4.9' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version:'4.5.9' - compile group: 'org.json', name: 'json', version:'20160810' - compile group: 'com.google.code.gson', name: 'gson', version:'2.8.5' - compile group: 'commons-beanutils', name: 'commons-beanutils', version:'1.9.3' - compile group: 'commons-io', name: 'commons-io', version:'2.6' - compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.26' - - testCompile group: 'junit', name: 'junit', version:'4.12' - testCompile group: 'com.github.tomakehurst', name: 'wiremock', version:'2.23.2' - testCompile group: 'org.slf4j', name: 'slf4j-jdk14', version:'1.7.26' + implementation group: 'com.mashape.unirest', name: 'unirest-java', version: '1.4.9' + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.9' + implementation group: 'org.json', name: 'json', version: '20160810' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + implementation group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.3' + implementation group: 'commons-io', name: 'commons-io', version: '2.6' + implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.26' + + testImplementation group: 'junit', name: 'junit', version: '4.12' + testImplementation group: 'com.github.tomakehurst', name: 'wiremock', version: '2.23.2' + testImplementation group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.26' codacy 'com.github.codacy:codacy-coverage-reporter:-SNAPSHOT' } + +test { + useJUnit() +} + // custom tasks for creating source jar task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' @@ -123,7 +111,7 @@ artifacts { //task to send coverage data to Codacy task sendCoverageToCodacy(type: JavaExec, dependsOn: jacocoTestReport) { - main = "com.codacy.CodacyCoverageReporter" + mainClass = "com.codacy.CodacyCoverageReporter" classpath = configurations.codacy args = [ "report", @@ -135,25 +123,44 @@ task sendCoverageToCodacy(type: JavaExec, dependsOn: jacocoTestReport) { } task integrationTest(type: Test) { - testClassesDirs = project.sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath - // This is not needed, but I like to see which tests have run - testLogging { - events "passed", "skipped", "failed" - } + testLogging { + events "passed", "skipped", "failed" + } } publishing { publications { + gpr(MavenPublication) { + from(components.java) + } + /* + maven(MavenPublication) { + artifactId = rootProject.name + groupId = group + version getVersionName() - MyPublication(MavenPublication) { - //we have to change scope from runtime to compile. Especially for Gson-lib. - pom.withXml { - asNode().dependencies.'*'.findAll() { - it.scope.text() == 'runtime' && project.configurations.compile.allDependencies.find { dep -> - dep.name == it.artifactId.text() + pom { + name = 'Airtable.java' + description = 'This is a Java API client for Airtable' + url = 'https://github.com/Sybit-Education/airtable.java' + licenses { + license { + name = 'MIT License' + url = 'https://opensource.org/licenses/MIT' + } + } + developers { + developer { + id = 'stritti' + name = 'Stephan Strittmatter' + email = 'stephan.strittmatter@sybit.de' } - }.each { it.scope*.value = 'compile' } + } + scm { + connection = 'scm:git:git://Sybit-Education/airtable.java.git' + developerConnection = 'scm:git:ssh://Sybit-Education/airtable.java.git' + url = 'https://github.com/Sybit-Education/airtable.java' + } } if (plugins.hasPlugin('war')) { @@ -161,39 +168,32 @@ publishing { } else { from components.java } - groupId group - artifactId rootProject.name - version getVersionName() + + //we have to change scope from runtime to compile. Especially for Gson-lib. + pom.withXml { + asNode().dependencies.'*'.findAll() { + it.scope.text() == 'runtime' && project.configurations.implementation.allDependencies.find { dep -> + dep.name == it.artifactId.text() + } + }.each { it.scope*.value = 'implementation' } + } artifact sourcesJar artifact javadocJar } + */ } -} - -bintray { - user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') - key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') - - publications = ['MyPublication'] - //configurations = ['archives'] - - dryRun = false //Whether to run this as dry-run, without deploying - publish = true //If version should be auto published after an upload - - pkg { - repo = 'maven' - userOrg = 'sybit-education' - name = 'airtable.java' - licenses = ['MIT License'] - vcsUrl = 'https://github.com/Sybit-Education/airtable.java.git' - version { - name = getVersionName() - released = new Date() + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/Sybit-Education/airtable.java") + credentials { + username = project.findProperty("gpr.user") ?: System.getenv("ACTION_DEPLOY_USER") + password = project.findProperty("gpr.key") ?: System.getenv("ACTION_DEPLOY_TOKEN") + } } } } integrationTest.mustRunAfter test - diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..6a390b7 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +signing.keyId=${{ secrets.GPG_SECRET_KEY }} +signing.password=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} +signing.secretKeyRingFile= \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 778aa9b..41d9927 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 82acbe8..00e33ed 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Jul 11 10:56:19 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 4453cce..1b6c787 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save ( ) { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/java/com/sybit/airtable/exception/package-info.java b/src/main/java/com/sybit/airtable/exception/package-info.java deleted file mode 100644 index 68ced38..0000000 --- a/src/main/java/com/sybit/airtable/exception/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/* - * The MIT License (MIT) - * Copyright (c) 2017 Sybit GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - */ -package com.sybit.airtable.exception; \ No newline at end of file diff --git a/src/main/java/com/sybit/airtable/package-info.java b/src/main/java/com/sybit/airtable/package-info.java deleted file mode 100644 index 32a97a5..0000000 --- a/src/main/java/com/sybit/airtable/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/* - * The MIT License (MIT) - * Copyright (c) 2017 Sybit GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - */ -package com.sybit.airtable; \ No newline at end of file diff --git a/src/main/java/com/sybit/airtable/vo/package-info.java b/src/main/java/com/sybit/airtable/vo/package-info.java deleted file mode 100644 index 30c3efa..0000000 --- a/src/main/java/com/sybit/airtable/vo/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/* - * The MIT License (MIT) - * Copyright (c) 2017 Sybit GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - */ -package com.sybit.airtable.vo; \ No newline at end of file