diff --git a/.github/workflows/cordova-CI.yml b/.github/workflows/cordova-CI.yml deleted file mode 100644 index 7570744..0000000 --- a/.github/workflows/cordova-CI.yml +++ /dev/null @@ -1,30 +0,0 @@ - -name: Cordova CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./plugin - - strategy: - matrix: - node-version: [10.x, 12.x, 14.x, 15.x] - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build --if-present - - run: npm test diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..77a3921 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,46 @@ + +name: "Build and Test" +on: [push, pull_request] +jobs: + js-test: + name: "JS Tests" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./plugin + strategy: + matrix: + node-version: [10.x, 12.x, 14.x, 15.x] + steps: + - name: "Checkout Branch" + uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: "NPM Build" + run: npm ci; npm run build --if-present + - name: "NPM Test" + run: npm test + android-test: + name: "Android Unit Tests" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./plugin + steps: + - name: "Checkout Branch" + uses: actions/checkout@v2 + - name: "Install JDK 11" + uses: actions/setup-java@v2 + with: + distribution: "zulu" + java-version: "11" + - name: "Install NPM" + uses: actions/setup-node@v1 + - name: "NPM Build" + run: | + npm ci + npm run build --if-present + - name: "Run Unit Tests" + run: ./gradlew test \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..62a797a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,85 @@ +name: "Release" +on: + workflow_dispatch: + inputs: + dryRun: + description: "Do a dry run to preview instead of a real release [true/false]" + required: true + default: "false" + +jobs: + semantic-release: + name: "Semantic Release" + runs-on: macos-latest + env: + GITHUB_TOKEN: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} + GIT_AUTHOR_NAME: mparticle-bot + GIT_AUTHOR_EMAIL: developers@mparticle.com + GIT_COMMITTER_NAME: mparticle-bot + GIT_COMMITTER_EMAIL: developers@mparticle.com + steps: + - name: "Checkout public main branch" + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: main + # - name: "Import GPG Key" + # uses: crazy-max/ghaction-import-gpg@v4 + # with: + # gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + # passphrase: ${{ secrets.GPG_PASSPHRASE }} + # git_user_signingkey: true + # git_commit_gpgsign: true + - name: "Semantic Release --dry-run" + if: ${{ github.event.inputs.dryRun == 'true' }} + run: | + npx \ + -p lodash \ + -p semantic-release@17 \ + -p @semantic-release/changelog@5 \ + -p @semantic-release/git@9 \ + -p @semantic-release/exec@5 \ + semantic-release --dry-run + - name: "Semantic Release" + if: ${{ github.event.inputs.dryRun == 'false' }} + run: | + npx \ + -p lodash \ + -p semantic-release@17 \ + -p @semantic-release/changelog@5 \ + -p @semantic-release/git@9 \ + -p @semantic-release/exec@5 \ + semantic-release + - name: "Push automated release commits to release branch" + if: ${{ github.event.inputs.dryRun == 'false' }} + run: | + git push origin main + + npm-release: + name: "Upload to NPM" + runs-on: ubuntu-latest + needs: semantic-release + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + defaults: + run: + working-directory: ./plugin + steps: + - name: "Checkout public main branch" + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: main + - name: "Install Node" + uses: actions/setup-node@v1 + - name: "NPM Build" + run: | + npm ci + npm run build --if-present + - name: "Upload to NPM" + if: ${{ github.event.inputs.dryRun == 'false' }} + run: | + touch .npmrc + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc + npm publish + diff --git a/.github/workflows/reusable-workflows.yml b/.github/workflows/reusable-workflows.yml new file mode 100644 index 0000000..a0a070e --- /dev/null +++ b/.github/workflows/reusable-workflows.yml @@ -0,0 +1,15 @@ +name: "PR Reusable Checks" + +on: + pull_request: + +jobs: + pr-branch-check-name: + name: "Check PR for semantic branch name" + uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-check-name.yml@stable + pr-title-check: + name: "Check PR for semantic title" + uses: mParticle/mparticle-workflows/.github/workflows/pr-title-check.yml@stable + pr-branch-target-gitflow: + name: "Check PR for semantic target branch" + uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-target-continuous.yml@stable \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1c1758e..8f2b58b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Built application files *.apk *.ap_ +*.bak # Files for the ART/Dalvik VM *.dex diff --git a/.scripts/release.sh b/.scripts/release.sh new file mode 100755 index 0000000..99fbb05 --- /dev/null +++ b/.scripts/release.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +: ${1?"Version missing - usage: $0 x.y.z"} + +#update build.gradle +line="\\\"version\\\":.*\\\".*\\\"/\\\"version\\\": \\\"$1\\\"" + +sed -i '.bak' "s/$line/g" plugin/package.json + +#commit the version bump, tag, and push to private and public +git add plugin/package.json diff --git a/README.md b/README.md index b8650c0..0ee1114 100755 --- a/README.md +++ b/README.md @@ -104,28 +104,26 @@ Please see [Identity](http://docs.mparticle.com/developers/sdk/ios/identity/) fo 2. Call `start` from the `onCreate` method of your app's `Application` class. It's crucial that the SDK be started here for proper session management. If you don't already have an `Application` class, create it and then specify its fully-qualified name in the `` tag of your app's `AndroidManifest.xml`. -```java +```kotlin package com.example.myapp; import android.app.Application; import com.mparticle.MParticle; -public class MyApplication extends Application { - @Override - public void onCreate() { - super.onCreate(); - MParticleOptions options = MParticleOptions.builder(this) - .credentials("REPLACE ME WITH KEY","REPLACE ME WITH SECRET") - .setLogLevel(MParticle.LogLevel.VERBOSE) +class MyApplication : Application() { + override fun onCreate() { + super.onCreate() + val options: MParticleOptions = MParticleOptions.builder(this) + .credentials("REPLACE ME WITH KEY", "REPLACE ME WITH SECRET") + .logLevel(MParticle.LogLevel.VERBOSE) .identify(identifyRequest) .identifyTask( - new BaseIdentityTask() - .addFailureListener(this) - .addSuccessListener(this) - ) - .build(); - - MParticle.start(options); + BaseIdentityTask() + .addFailureListener() { response -> } + .addSuccessListener { result -> } + ) + .build() + MParticle.start(options) } } ``` diff --git a/plugin/build.gradle b/plugin/build.gradle deleted file mode 100644 index 1ea4bd0..0000000 --- a/plugin/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - jcenter() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/plugin/gradle.properties b/plugin/gradle.properties index aac7c9b..bddf6c0 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1,17 +1,16 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html - +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +#Thu May 05 14:30:48 EDT 2022 +org.gradle.jvmargs=-Xmx1536m +android.useAndroidX=true +android.enableJetifier=true diff --git a/plugin/gradle/wrapper/gradle-wrapper.properties b/plugin/gradle/wrapper/gradle-wrapper.properties index de790a9..5763170 100644 --- a/plugin/gradle/wrapper/gradle-wrapper.properties +++ b/plugin/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip diff --git a/plugin/plugin.xml b/plugin/plugin.xml index 7db9189..304bc0f 100755 --- a/plugin/plugin.xml +++ b/plugin/plugin.xml @@ -13,7 +13,7 @@ - + diff --git a/plugin/settings.gradle b/plugin/settings.gradle deleted file mode 100644 index c3de63e..0000000 --- a/plugin/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':src:android' \ No newline at end of file diff --git a/plugin/settings.gradle.kts b/plugin/settings.gradle.kts new file mode 100644 index 0000000..6eeb660 --- /dev/null +++ b/plugin/settings.gradle.kts @@ -0,0 +1,10 @@ +include(":src:android") +pluginManagement { + repositories { + google() + mavenCentral() + } + plugins { + id("com.android.library") version ("7.1.3") + } +} \ No newline at end of file diff --git a/plugin/src/android/build.gradle b/plugin/src/android/build.gradle index 15fad78..16fdc51 100644 --- a/plugin/src/android/build.gradle +++ b/plugin/src/android/build.gradle @@ -1,25 +1,30 @@ -apply plugin: 'com.android.library' +plugins { + id("com.android.library") +} android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdk = 31 defaultConfig { - minSdkVersion 15 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" + minSdk = 15 + targetSdk = 31 + versionCode = 1 + versionName = "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } - buildTypes { +} - } +repositories { + google() + mavenCentral() } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - testCompile 'org.json:json:20080701' - testCompile "org.mockito:mockito-core:1.+" - provided 'org.apache.cordova:framework:7.1.0' - provided 'com.mparticle:android-core:5.27.0' + compileOnly("org.apache.cordova:framework:10.1.2") + compileOnly("com.mparticle:android-core:5.38.1") + + testImplementation("junit:junit:4.13.2") + testImplementation("org.json:json:20220320") + testImplementation("org.mockito:mockito-core:4.5.1") + testImplementation("org.apache.cordova:framework:10.1.2") + testImplementation("com.mparticle:android-core:5.38.1") } diff --git a/plugin/src/android/src/main/java/com/mparticle/cordova/MParticleCordovaPlugin.java b/plugin/src/android/src/main/java/com/mparticle/cordova/MParticleCordovaPlugin.java index f2e569d..c629795 100644 --- a/plugin/src/android/src/main/java/com/mparticle/cordova/MParticleCordovaPlugin.java +++ b/plugin/src/android/src/main/java/com/mparticle/cordova/MParticleCordovaPlugin.java @@ -9,7 +9,11 @@ import com.mparticle.commerce.Product; import com.mparticle.commerce.TransactionAttributes; import com.mparticle.commerce.Promotion; +import com.mparticle.consent.CCPAConsent; +import com.mparticle.consent.ConsentState; +import com.mparticle.consent.GDPRConsent; import com.mparticle.identity.*; +import com.mparticle.internal.Logger; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; @@ -18,7 +22,10 @@ import org.json.JSONException; import org.json.JSONObject; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.Dictionary; import java.util.HashMap; import java.util.Iterator; @@ -65,16 +72,24 @@ public boolean execute(String action, final JSONArray args, final CallbackContex getUserIdentities(args, callbackContext); return true; } else if (action.equals("addGDPRConsentState")) { - addGDPRConsentState(args, callbackContext); + try { + addGDPRConsentState(args); + } catch (ParseException e) { + Logger.warning(e, "failed to add GDPRConsentState"); + } return true; } else if (action.equals("removeGDPRConsentState")) { - removeGDPRConsentState(args, callbackContext); + removeGDPRConsentState(args); return true; } else if (action.equals("addCCPAConsentState")) { - addCCPAConsentState(args, callbackContext); + try { + addCCPAConsentState(args); + } catch (ParseException e) { + Logger.warning(e, "failed to add CCPAConsentState"); + } return true; } else if (action.equals("removeCCPAConsentState")) { - removeCCPAConsentState(args, callbackContext); + removeCCPAConsentState(args); return true; } else { return false; @@ -338,49 +353,49 @@ public void getUserIdentities(final JSONArray args, final CallbackContext callba callbackContext.sendPluginResult(pluginResult); } - public void addGDPRConsentState(final JSONArray args) throws JSONException { + public void addGDPRConsentState(final JSONArray args) throws JSONException, ParseException { MParticleUser user = MParticle.getInstance().Identity().getCurrentUser(); final JSONObject map = args.getJSONObject(0); String purpose = args.getString(1); if (user != null && map != null && purpose != null) { - newConsent = ConvertGDPRConsent(map); + GDPRConsent newConsent = ConvertGDPRConsent(map); ConsentState state = ConsentState.builder() .addGDPRConsentState(purpose, newConsent) .build(); - user.setConsentState(newConsent); + user.setConsentState(state); } } - public void removeGDPRConsentState(final JSONArray args) throws JSONException { + public void removeGDPRConsentState(final JSONArray state) throws JSONException { MParticleUser user = MParticle.getInstance().Identity().getCurrentUser(); - String purpose = args.getString(0); + String purpose = state.getString(0); if (user != null && purpose != null) { - ConsentState updatedState = ConsentState.withConsentState(state) - .removeGDPRConsentState("parental") + ConsentState updatedState = ConsentState.withConsentState(user.getConsentState()) + .removeGDPRConsentState(purpose) .build(); user.setConsentState(updatedState); } } - public void addCCPAConsentState(final JSONArray args) throws JSONException { + public void addCCPAConsentState(final JSONArray args) throws JSONException, ParseException { MParticleUser user = MParticle.getInstance().Identity().getCurrentUser(); final JSONObject map = args.getJSONObject(0); if (user != null && map != null) { - newConsent = ConvertCCPAConsent(map); + CCPAConsent newConsent = ConvertCCPAConsent(map); ConsentState state = ConsentState.builder() .setCCPAConsentState(newConsent) .build(); - user.setConsentState(newConsent); + user.setConsentState(state); } } - public void removeCCPAConsentState(final JSONArray args) throws JSONException { + public void removeCCPAConsentState(final JSONArray state) throws JSONException { MParticleUser user = MParticle.getInstance().Identity().getCurrentUser(); if (user != null) { - ConsentState updatedState = ConsentState.withConsentState(state) + ConsentState updatedState = ConsentState.withConsentState(user.getConsentState()) .removeCCPAConsentState() .build(); @@ -761,14 +776,14 @@ private static String ConvertPromotionActionType(int promotionActionType) { } } - private static GDPRConsent ConvertGDPRConsent(JSONObject map) throws JSONException { - String dateStr = obj.getString("timestamp"); + private static GDPRConsent ConvertGDPRConsent(JSONObject map) throws JSONException, ParseException { + String dateStr = map.getString("timestamp"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date timestamp = sdf.parse(dateStr); GDPRConsent consent = GDPRConsent.builder(map.getBoolean("consented")) .document(map.getString("document")) - .timestamp(timestamp) + .timestamp(timestamp.getTime()) .location(map.getString("location")) .hardwareId(map.getString("hardwareId")) .build(); @@ -776,14 +791,14 @@ private static GDPRConsent ConvertGDPRConsent(JSONObject map) throws JSONExcepti return consent; } - private static GDPRConsent ConvertGDPRConsent(JSONObject map) throws JSONException { - String dateStr = obj.getString("timestamp"); + private static CCPAConsent ConvertCCPAConsent(JSONObject map) throws JSONException, ParseException { + String dateStr = map.getString("timestamp"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date timestamp = sdf.parse(dateStr); CCPAConsent consent = CCPAConsent.builder(map.getBoolean("consented")) .document(map.getString("document")) - .timestamp(timestamp) + .timestamp(timestamp.getTime()) .location(map.getString("location")) .hardwareId(map.getString("hardwareId")) .build(); diff --git a/release.config.js b/release.config.js new file mode 100644 index 0000000..6232a1b --- /dev/null +++ b/release.config.js @@ -0,0 +1,51 @@ +module.exports = { + branches: ["main"], + tagFormat: "${version}", + plugins: [ + [ + "@semantic-release/commit-analyzer", + { + preset: "angular", + releaseRules: [ + {type: 'feat', release: 'minor'}, + {type: 'ci', release: 'patch'}, + {type: 'fix', release: 'patch'}, + {type: 'docs', release: 'patch'}, + {type: 'test', release: 'patch'}, + {type: 'refactor', release: 'patch'}, + {type: 'style', release: 'patch'}, + {type: 'build', release: 'patch'}, + {type: 'chore', release: 'patch'}, + {type: 'revert', release: 'patch'} + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + preset: "angular", + }, + ], + [ + "@semantic-release/changelog", + { + changelogFile: "CHANGELOG.md", + }, + ], + [ + "@semantic-release/exec", + { + prepareCmd: "sh ./.scripts/release.sh ${nextRelease.version}", + }, + ], + ["@semantic-release/github"], + [ + "@semantic-release/git", + { + assets: ["plugin/package.json", "CHANGELOG.md"], + message: + "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}", + }, + ], + ], +};