问:
Custom notification channel for FCM in flutter
I am trying to customize notification sound of firebase_messaging in flutter. On foreground I am implementing flutter_local_notifications package to deliver notification where I have setup custom sound and vibration. But in case of background, notification is handled by default notification channel. Is there any way I can create a notification channel or use the one I just created using flutter_local_notifications package?</p>

答:
In Flutter you can create android notification channel yourself thru MainActivity.kt or MainActivity.java file depending whichever you have in your project Android folder. Good guide here - using MainActivity.kt which is easy and working, tried myself - it works:

```<code>package com.example.new_channel //Your own package name
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.app.NotificationManager;
import android.app.NotificationChannel;
import android.net.Uri;
import android.media.AudioAttributes;
import android.content.ContentResolver;

class MainActivity: FlutterActivity() {
  private val CHANNEL = "somethinguniqueforyou.com/channel_test" //The channel name you set in your main.dart file

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      // Note: this method is invoked on the main thread.
      call, result ->

      if (call.method == "createNotificationChannel"){
        val argData = call.arguments as java.util.HashMap<String, String>
          val completed = createNotificationChannel(argData)
          if (completed == true){
              result.success(completed)
          }
          else{
              result.error("Error Code", "Error Message", null)
          }
      } else {
        result.notImplemented()
      }
    }

  }

    private fun createNotificationChannel(mapData: HashMap<String,String>): Boolean {
        val completed: Boolean
        if (VERSION.SDK_INT >= VERSION_CODES.O) {
            // Create the NotificationChannel
            val id = mapData["id"]
            val name = mapData["name"]
            val descriptionText = mapData["description"]
            val sound = "your_sweet_sound"
            val importance = NotificationManager.IMPORTANCE_HIGH
            val mChannel = NotificationChannel(id, name, importance)
            mChannel.description = descriptionText

            val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"+ getApplicationContext().getPackageName() + "/raw/your_sweet_sound");
            val att = AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_NOTIFICATION)
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            .build();

            mChannel.setSound(soundUri, att)
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(mChannel)
            completed = true
        }
        else{
            completed = false
        }
        return completed
    }
}
</code>```

And this is with MainActivity java:

```<code>import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import android.media.AudioAttributes;
import androidx.core.app.NotificationCompat;
import android.net.Uri;
import android.content.ContentResolver;
this is a code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(“new_email_arrived_channel”, “My Emailer”, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setShowBadge(true);
notificationChannel.setDescription(“”);
AudioAttributes att = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
notificationChannel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + “://” + getPackageName() + “/raw/bell”), att);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{400, 400});
notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
</code>```

In Flutter side you may need trigger for initiating the process and naming the notification channel. It is from the same source above:

```<code>String _statusText = "Waiting...";
  final String _finished = "Finished creating channel";
  final String _error = "Error while creating channel";

  static const MethodChannel _channel =
      MethodChannel('somethinguniqueforyou.com/channel_test');

  Map<String, String> channelMap = {
    "id": "CHAT_MESSAGES",
    "name": "Chats",
    "description": "Chat notifications",
  };

  void _createNewChannel() async {
    try {
      await _channel.invokeMethod('createNotificationChannel', channelMap);
      setState(() {
        _statusText = _finished;
      });
    } on PlatformException catch (e) {
      _statusText = _error;
      print(e);
    }
  }
</code>```

Now for all Android versions  you need only this format notification payload:

```<code>"notification": {
            "body": "Test notification",
            "title": "Test Test Test",
            "click_action": "FLUTTER_NOTIFICATION_CLICK",

            "sound": "your_custom_sound"
            "android_channel_id": "channel_id_youcreated",
},

          'to':
              "",
        },
</code>```

Sound file name is not necessary in the above given notification payload if you assigned that sound to your notification channel thru MainActivity.kt or java file. However, it is necessary for older Android versions as they will use the sound file directly.

Body:
In Flutter you can create android notification channel yourself thru MainActivity.kt or MainActivity.java file depending whichever you have in your project Android folder. Good guide here - using MainActivity.kt which is easy and working, tried myself - it works:

package com.example.new_channel //Your own package name
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.app.NotificationManager;
import android.app.NotificationChannel;
import android.net.Uri;
import android.media.AudioAttributes;
import android.content.ContentResolver;

class MainActivity: FlutterActivity() {
  private val CHANNEL = "somethinguniqueforyou.com/channel_test" //The channel name you set in your main.dart file

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      // Note: this method is invoked on the main thread.
      call, result ->

      if (call.method == "createNotificationChannel"){
        val argData = call.arguments as java.util.HashMap<String, String>
          val completed = createNotificationChannel(argData)
          if (completed == true){
              result.success(completed)
          }
          else{
              result.error("Error Code", "Error Message", null)
          }
      } else {
        result.notImplemented()
      }
    }

  }

    private fun createNotificationChannel(mapData: HashMap<String,String>): Boolean {
        val completed: Boolean
        if (VERSION.SDK_INT >= VERSION_CODES.O) {
            // Create the NotificationChannel
            val id = mapData["id"]
            val name = mapData["name"]
            val descriptionText = mapData["description"]
            val sound = "your_sweet_sound"
            val importance = NotificationManager.IMPORTANCE_HIGH
            val mChannel = NotificationChannel(id, name, importance)
            mChannel.description = descriptionText

            val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"+ getApplicationContext().getPackageName() + "/raw/your_sweet_sound");
            val att = AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_NOTIFICATION)
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            .build();

            mChannel.setSound(soundUri, att)
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(mChannel)
            completed = true
        }
        else{
            completed = false
        }
        return completed
    }
}

And this is with MainActivity java:

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import android.media.AudioAttributes;
import androidx.core.app.NotificationCompat;
import android.net.Uri;
import android.content.ContentResolver;
this is a code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(“new_email_arrived_channel”, “My Emailer”, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setShowBadge(true);
notificationChannel.setDescription(“”);
AudioAttributes att = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
notificationChannel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + “://” + getPackageName() + “/raw/bell”), att);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{400, 400});
notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}

In Flutter side you may need trigger for initiating the process and naming the notification channel. It is from the same source above:

String _statusText = "Waiting...";
  final String _finished = "Finished creating channel";
  final String _error = "Error while creating channel";

  static const MethodChannel _channel =
      MethodChannel('somethinguniqueforyou.com/channel_test');

  Map<String, String> channelMap = {
    "id": "CHAT_MESSAGES",
    "name": "Chats",
    "description": "Chat notifications",
  };

  void _createNewChannel() async {
    try {
      await _channel.invokeMethod('createNotificationChannel', channelMap);
      setState(() {
        _statusText = _finished;
      });
    } on PlatformException catch (e) {
      _statusText = _error;
      print(e);
    }
  }

Now for all Android versions  you need only this format notification payload:

"notification": {
            "body": "Test notification",
            "title": "Test Test Test",
            "click_action": "FLUTTER_NOTIFICATION_CLICK",

            "sound": "your_custom_sound"
            "android_channel_id": "channel_id_youcreated",
},

          'to':
              "",
        },

Sound file name is not necessary in the above given notification payload if you assigned that sound to your notification channel thru MainActivity.kt or java file. However, it is necessary for older Android versions as they will use the sound file directly.


来源: stackexchange

问:
Connection Reset error with Google Drive APIs
I am using a Google Service account to create Google Drive resources.

However when i try to execute Drive create folder API i get connection reset error.

I can confirm that the credentials for the service account are properly configured and loaded in my program.

Below is the code and the stacktrace

```<code> public static File createFolder(Drive driveService) {
    File fileMetadata = new File();
    fileMetadata.setName("Testing");
    fileMetadata.setMimeType("application/vnd.google-apps.folder");

    File file =null;
    try {
        file = driveService.files().create(fileMetadata)
            .setFields("id")
            .execute();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        System.out.println("Exception here in create folder");
        e.printStackTrace();
    }
    System.out.println("Folder ID: " + file.getId());

    return file;
}
</code>```

Below is how the credentials are read and instance of Drive is created

```<code>/**
 * Creates credentials for the service account
 * @param HTTP_TRANSPORT
 * @return
 * @throws IOException
 */
private static Credential getServiceAccountCredential(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
     InputStream in = DriveQuickStart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
     if (in == null) {
         throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
     }
     GoogleCredential credential = GoogleCredential.fromStream(in)
             .createScoped(SCOPES);
     System.out.println("loaded credential " + credential.getServiceAccountId()) ;
     return credential;
}

public static void main(String... args) throws IOException, GeneralSecurityException {
    // Build a new authorized API client service.
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    SCOPES.add(DriveScopes.DRIVE_FILE);
    Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getServiceAccountCredential(HTTP_TRANSPORT))
            .setApplicationName(APPLICATION_NAME)
            .build();
    //create folder

   File parentFolder =  createFolder(service);
</code>```

Stacktrace

```<code>    Exception here in create folder
javax.net.ssl.SSLException: Connection reset
        at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:127)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:369)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:312)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:307)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:144)
        at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1488)
        at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1394)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:441)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:412)
        at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
        at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1375)
        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1350)
        at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:220)
        at com.google.api.client.http.javanet.NetHttpRequest.execute(NetHttpRequest.java:77)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:981)
        at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:283)
        at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
        at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:394)
        at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
        at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
        at DriveQuickStart.createFolder(DriveQuickStart.java:138)
        at DriveQuickStart.main(DriveQuickStart.java:103)
        Suppressed: java.net.SocketException: Broken pipe
                at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)
                at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
                at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826)
                at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1051)
                at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:82)
                at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:400)
                ... 25 more
Caused by: java.net.SocketException: Connection reset
        at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323)
        at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
        at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
        at java.base/java.net.Socket$SocketInputStream.read(Socket.java:981)
        at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:478)
        at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472)
        at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:110)
        ... 22 more
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.google.api.services.drive.model.File.getId()" because "file" is null
        at DriveQuickStart.createFolder(DriveQuickStart.java:144)
        at DriveQuickStart.main(DriveQuickStart.java:103)
</code>```

答:
you are using DriveScopes.DRIVE_FILE this scope can act a bit strange sometimes.  Its basicly used to lock an application and only allow it to edit files that it created itself.  When creating folders I have found it can act very strangely if the directory its creating is not inside a directory it created in the first place.

https://www.googleapis.com/auth/drive  See, edit, create, and delete all of your Google Drive files

The solution is to just use. DriveScopes.DRIVE  technically you are using a service account anyway there is really no reason to limit it.

https://www.googleapis.com/auth/drive.file    View and manage Google Drive files and folders that you have opened or created with this app

ContentLicense: CC BY-SA 4.0

Body:
I am using a Google Service account to create Google Drive resources.

However when i try to execute Drive create folder API i get connection reset error.

I can confirm that the credentials for the service account are properly configured and loaded in my program.

Below is the code and the stacktrace

 public static File createFolder(Drive driveService) {
    File fileMetadata = new File();
    fileMetadata.setName("Testing");
    fileMetadata.setMimeType("application/vnd.google-apps.folder");

    File file =null;
    try {
        file = driveService.files().create(fileMetadata)
            .setFields("id")
            .execute();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        System.out.println("Exception here in create folder");
        e.printStackTrace();
    }
    System.out.println("Folder ID: " + file.getId());

    return file;
}

Below is how the credentials are read and instance of Drive is created

/**
 * Creates credentials for the service account
 * @param HTTP_TRANSPORT
 * @return
 * @throws IOException
 */
private static Credential getServiceAccountCredential(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
     InputStream in = DriveQuickStart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
     if (in == null) {
         throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
     }
     GoogleCredential credential = GoogleCredential.fromStream(in)
             .createScoped(SCOPES);
     System.out.println("loaded credential " + credential.getServiceAccountId()) ;
     return credential;
}

public static void main(String... args) throws IOException, GeneralSecurityException {
    // Build a new authorized API client service.
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    SCOPES.add(DriveScopes.DRIVE_FILE);
    Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getServiceAccountCredential(HTTP_TRANSPORT))
            .setApplicationName(APPLICATION_NAME)
            .build();
    //create folder

   File parentFolder =  createFolder(service);

Stacktrace

    Exception here in create folder
javax.net.ssl.SSLException: Connection reset
        at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:127)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:369)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:312)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:307)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:144)
        at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1488)
        at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1394)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:441)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:412)
        at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
        at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1375)
        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1350)
        at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:220)
        at com.google.api.client.http.javanet.NetHttpRequest.execute(NetHttpRequest.java:77)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:981)
        at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:283)
        at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
        at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:394)
        at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
        at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
        at DriveQuickStart.createFolder(DriveQuickStart.java:138)
        at DriveQuickStart.main(DriveQuickStart.java:103)
        Suppressed: java.net.SocketException: Broken pipe
                at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)
                at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
                at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826)
                at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1051)
                at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:82)
                at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:400)
                ... 25 more
Caused by: java.net.SocketException: Connection reset
        at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323)
        at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
        at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
        at java.base/java.net.Socket$SocketInputStream.read(Socket.java:981)
        at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:478)
        at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472)
        at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:110)
        ... 22 more
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.google.api.services.drive.model.File.getId()" because "file" is null
        at DriveQuickStart.createFolder(DriveQuickStart.java:144)
        at DriveQuickStart.main(DriveQuickStart.java:103)


问:
How to let Query are performed at the component onmount and triggered by user event later?
My page has a search form and table with a paginator. So I have two states: searchCondition and pagination. I need to query the channelList when the page component is mounted.

When the user clicks the paginator, the pagination state will be changed so that a new channelList query will be made. That's ok.

The search form includes two input form items and a "Query" button. The channelList query also can be triggered by the user click event.

But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

I read the doc Dependent Queries, there is an enabled option, but don't think it can solve my issue.

Currently:

Send the query on the component mount. - done!

Made a new query when pagination state changed - done!

User types something in the input form item(this will cause the searchCondition state changed) and clicks the "Query" button, then made a new query - undone.

When sending the channelList query, I will make the API parameters by merging searchCondition and pagination states.

```<code>const { searchCondition, setSearchCondition } = useSearchCondition(initialSearchCondition);
const { pagination, setPagination } = usePagination();
const channelListQuery = useQuery(
  ['channelList', pagination, searchCondition],
  async () => {
    const getChannelListParams = omitFalsyFields({
      currentPage: pagination.current,
      pageSize: pagination.pageSize,
      ...searchCondition,
    });
    const { code, result } = await apis.channel.getChannelList(getChannelListParams);
    if (code === '0') {
      return result;
    }
  },
  { initialData: { totalItem: 0, resultList: [] }, refetchOnWindowFocus: false, keepPreviousData: true },
);
</code>```

答:
But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

What you need for this to happen is to have two state. One inside the form so that users can type (this can also be an uncontrolled form), and another one that you set when the user clicks the "Query" button.

That second state is what your query should "listen to" - by being part of the query key. Pseudo code:

```<code>function App() {
 const [search, setSearch] = useState()
 const { data } = useQuery(['channelList', search], () => getChannelList(search), { enabled: !!search }

 <SearchFrom onSubmit={setSearch} />
}
</code>```

by making sure to only call setSearch when the user clicks the Query button, you make sure that the query doesn't fire on every click.

A good place to store "applied searches" is also the url: When the user clicks the button, you write to the url, and the query subscribes to that.

This also makes your urls sharable, which is nice.

I have a short example on that here:

https://codesandbox.io/s/react-query-url-filtering-h0ikw?file=/src/App.tsx

The filter form itself is uncontrolled - but it doesn't really matter how you solve it, it's "internal" to the filter form. The "global" client state lives in the url.

Body:
My page has a search form and table with a paginator. So I have two states: searchCondition and pagination. I need to query the channelList when the page component is mounted.

When the user clicks the paginator, the pagination state will be changed so that a new channelList query will be made. That's ok.

The search form includes two input form items and a "Query" button. The channelList query also can be triggered by the user click event.

But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

I read the doc Dependent Queries, there is an enabled option, but don't think it can solve my issue.

Currently:

Send the query on the component mount. - done!

Made a new query when pagination state changed - done!

User types something in the input form item(this will cause the searchCondition state changed) and clicks the "Query" button, then made a new query - undone.

When sending the channelList query, I will make the API parameters by merging searchCondition and pagination states.

const { searchCondition, setSearchCondition } = useSearchCondition(initialSearchCondition);
const { pagination, setPagination } = usePagination();
const channelListQuery = useQuery(
  ['channelList', pagination, searchCondition],
  async () => {
    const getChannelListParams = omitFalsyFields({
      currentPage: pagination.current,
      pageSize: pagination.pageSize,
      ...searchCondition,
    });
    const { code, result } = await apis.channel.getChannelList(getChannelListParams);
    if (code === '0') {
      return result;
    }
  },
  { initialData: { totalItem: 0, resultList: [] }, refetchOnWindowFocus: false, keepPreviousData: true },
);

Body:
But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

What you need for this to happen is to have two state. One inside the form so that users can type (this can also be an uncontrolled form), and another one that you set when the user clicks the "Query" button.

That second state is what your query should "listen to" - by being part of the query key. Pseudo code:

function App() {
 const [search, setSearch] = useState()
 const { data } = useQuery(['channelList', search], () => getChannelList(search), { enabled: !!search }

 <SearchFrom onSubmit={setSearch} />
}

by making sure to only call setSearch when the user clicks the Query button, you make sure that the query doesn't fire on every click.

A good place to store "applied searches" is also the url: When the user clicks the button, you write to the url, and the query subscribes to that.

This also makes your urls sharable, which is nice.

I have a short example on that here:

https://codesandbox.io/s/react-query-url-filtering-h0ikw?file=/src/App.tsx

The filter form itself is uncontrolled - but it doesn't really matter how you solve it, it's "internal" to the filter form. The "global" client state lives in the url.


问:
The argument type 'Iterable<dynamic>' can't be assigned to the parameter type 'List<DropdownMenuItem<String>>?'

DropdownButton<String>(
              value: value,
              items: locationItems.map( DropdownMenuItem<String>(
                child: Text( widget.locationName ),
                value: "${widget.locationName}",
              )
              ),

```

答:
The argument type 'List<DropdownMenuItem>' can't be assigned to the parameter type 'List<DropdownMenuItem>?

Make your declared variable as the type above will solve your poblem


问:
Retrieve node from firebase without subnode
My app will display the offers for the user, and he can pick one and press join, which will send the email account to "Joiners" node.

But now my problem is I want him to see how many people have joined by counting "Joiners" node, without downloading all the emails that inside, of course.

So what I want again:

Retrieve "1231qq" Node without the Emails that in "Joiners" Node.

Count how many email that in "Joiners" node but without having to
make the user download the whole node with other user's emails

I don't know exactly if I'm missing a technology that Firebase offer to do that kind of registration or what, so I would love some advice here, and thank you for your efforts.

答:
The Firebase Realtime Database SDKs always load entire branches of the JSON. There is no way to get only a part of a node.

When you are looking for something like this, it typically means you need to heed the recommendation in the Firebase documentation to never nest multiple entity types under a single node. For example, you'll want to split the data in your screenshot over two top-level nodes like this:

```<code>Offers: {
  "1231qq": {
    done: 1231,
    name: "...",
    required: 3000,
    url: "..."
  }
},
OfferJoiners: {
  "1231qq": {
    1: "Email 1",
    2: "Email 2"
  }
}
</code>```

With the above structure, you can load just the offer details without the joiner from Offers. And when needed, you perform a second call to also load that offers joiners from OfferJoiners. The extra call does not cause as much overhead as you may think, since Firebase pipelines the requests over a single connection.

Body:
The Firebase Realtime Database SDKs always load entire branches of the JSON. There is no way to get only a part of a node.

When you are looking for something like this, it typically means you need to heed the recommendation in the Firebase documentation to never nest multiple entity types under a single node. For example, you'll want to split the data in your screenshot over two top-level nodes like this:

Offers: {
  "1231qq": {
    done: 1231,
    name: "...",
    required: 3000,
    url: "..."
  }
},
OfferJoiners: {
  "1231qq": {
    1: "Email 1",
    2: "Email 2"
  }
}

With the above structure, you can load just the offer details without the joiner from Offers. And when needed, you perform a second call to also load that offers joiners from OfferJoiners. The extra call does not cause as much overhead as you may think, since Firebase pipelines the requests over a single connection.


问:
How to conditionally set a Style variable in JSX (React)?
I am trying to conditonally set the border color of an image. Depending on the image name, I would like to set a different border color.

Here is my code:

```<code><div className="row">
    {asideData.imageGallery2.map( (img, i) => {
        return(
              <div className="col-md-6 col-lg-4">
                   // if img.name includes "Quartz" then make border color red
                   // if img.name includes "Flake" then make border color blue
                  <img src={img.img} alt="" key={img.name} style={{border:"5px solid #555"}}/>
               </div>)
               })}

</div>
</code>```

答:
Use a template literal in the string for the border property so you can use the conditional operator to alternate between the two possibilities.

Define the border in a separate variable beforehand so that it's not unreadable.

You aren't using the index parameter, so feel free to remove it.

```<code>asideData.imageGallery2.map((img) => {
    const border = `5px solid ${
        img.name.includes('Quartz')
        ? 'red'
        : img.name.includes('Flake')
            ? 'blue'
            : 'black' // default color
    }`;
    return (
        <div className="col-md-6 col-lg-4">
            <img src={img.img} alt="" key={img.name} style={{ border }} />
        </div>)
})
</code>```

Body:
I am trying to conditonally set the border color of an image. Depending on the image name, I would like to set a different border color.

Here is my code:

    {asideData.imageGallery2.map( (img, i) => {
        return(

                   // if img.name includes "Quartz" then make border color red
                   // if img.name includes "Flake" then make border color blue

)
               })}

Body:
Use a template literal in the string for the border property so you can use the conditional operator to alternate between the two possibilities.

Define the border in a separate variable beforehand so that it's not unreadable.

You aren't using the index parameter, so feel free to remove it.

asideData.imageGallery2.map((img) => {
    const border = `5px solid ${
        img.name.includes('Quartz')
        ? 'red'
        : img.name.includes('Flake')
            ? 'blue'
            : 'black' // default color
    }`;
    return (
        <div className="col-md-6 col-lg-4">
            <img src={img.img} alt="" key={img.name} style={{ border }} />
        </div>)
})


问:
Discord Bot isn't Activating
I am trying to run my discord bot but then I keep on getting:

```<code>raise ClientConnectorCertificateError(aiohttp.client_exceptions.ClientConnectorCertificateError:
Cannot connect to host discord.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)')]
</code>```

my code is

```<code>import discord
from discord.ext import commands

client = commands.Bot(command_prefix="$")

@client.event
async def on_ready():
    print("Bot is ready")

@client.command()
async def hello(ctx):
    await ctx.send("hey")

client.run("-------------------------------------")
</code>```

答:
Go to start, type internet explorer. Run as admin.

Go do https://discord.com click on the padlock icon.

Click view certificates

Click install certificates, choose either user or local machine.

Click Place all certificates in the following store

Choose Trusted Root Certification Authorities

Enjoy.

Title: Discord Bot isn't Activating

Body:
I am trying to run my discord bot but then I keep on getting:

raise ClientConnectorCertificateError(aiohttp.client_exceptions.ClientConnectorCertificateError:
Cannot connect to host discord.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)')]

my code is

import discord
from discord.ext import commands

client = commands.Bot(command_prefix="$")

@client.event
async def on_ready():
    print("Bot is ready")

@client.command()
async def hello(ctx):
    await ctx.send("hey")

client.run("-------------------------------------")


问:
Google Sheet Formula to extract from | separated hiearchy
I have a hierarchy list separated by |.  The hierarchy is listed as L1|L2|L3|L4|L5 ect.

I'm trying to create a formula to extract the L2 and L3 of the hierarchy.  The best method I've found for now is using the Text to Columns feature in Google Sheets but a formula would be ideal.  Giving some examples below.

答:
Another possible formula with regular expressions, where the first quantifier {n,n} decides which element to extract from the hierarchy, identifying the number of preceding characters that should not be considered: e.g. L2 = {3,3}. In the example is {6,6} for L3

L2 or L3 is the capturing group (\w+), extracted with $1

```<code>=arrayformula(if(C2:C="","",regexreplace(C2:C,"[\w+\|]{6,6}(\w+){1,1}[\w+\|]{3,}","$1")))
</code>```

Body:
Another possible formula with regular expressions, where the first quantifier {n,n} decides which element to extract from the hierarchy, identifying the number of preceding characters that should not be considered: e.g. L2 = {3,3}. In the example is {6,6} for L3

L2 or L3 is the capturing group (\w+), extracted with $1

=arrayformula(if(C2:C="","",regexreplace(C2:C,"[\w+\|]{6,6}(\w+){1,1}[\w+\|]{3,}","$1")))


问:
How can I get my app using Instagram Basic Display API pass app review?
This is now the 4th time I am sending my app for review. I want to use Instagram Basic Display API and therefore require instagram_graph_user_media permission to access media (and incidentally instagram_graph_user_profile). I have 2 test users, my personal IG account with a bunch of pics and a test user that I created with an empty feed. I can login with both users. But when the Instagram app reviewer is logging in, my app can't access their media. I successfully retrieve the access token but when comes the time to call the Graph API here is what happens:

https://graph.instagram.com/me/media?fields=media_type,media_url,permalink,thumbnail_url&access_token=IGQV....

returns

{"error":{"message":"Application does not have permission for this action","type":"IGApiException","code":10,"fbtrace_id":"A99vuaAC41DSvlt0Hxvcly-"}}

答:
Basic Display API review process is so bad its beyond words. I have been hitting the brick wall of their rejections for 3 weeks and almost got bald by pulling my hair in frustration. You really have to read between the lines to get a hint of what they are doing.

Turns out what the reviewer was doing is selecting "Continue with Facebook" on the Instagram Login screen and going that route (via Facebook login) instead of entering the instagram credentials directly. Only once I realized that I was able to pin point the problem. Interestingly though testing on the Simulator was fine but the problem only became apparent once I tested on the real device. The reason - simulator doesn't have neither Facebook app nor Instagram app installed, so it behaves differently versus the device where these apps get involved in the flow via deep linking.

The bottom line:

Test on real device.

Make sure to test both the direct Instagram log in and the "Continue with Facebook" option.

Test on the device with and without the Facebook and/or Instagram app installed.

Make sure to use brand new instance of WKWebView with non persistent data store to bring up the login screen, so that it doesn't have any cookies from previous logins:

```<code>        let configuration = WKWebViewConfiguration()
        configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
        let webView = WKWebView(frame: .zero, configuration: configuration)
</code>```

Pray the God of your choosing.

Body:
Basic Display API review process is so bad its beyond words. I have been hitting the brick wall of their rejections for 3 weeks and almost got bald by pulling my hair in frustration. You really have to read between the lines to get a hint of what they are doing.

Turns out what the reviewer was doing is selecting "Continue with Facebook" on the Instagram Login screen and going that route (via Facebook login) instead of entering the instagram credentials directly. Only once I realized that I was able to pin point the problem. Interestingly though testing on the Simulator was fine but the problem only became apparent once I tested on the real device. The reason - simulator doesn't have neither Facebook app nor Instagram app installed, so it behaves differently versus the device where these apps get involved in the flow via deep linking.

The bottom line:

Test on real device.

Make sure to test both the direct Instagram log in and the "Continue with Facebook" option.

Test on the device with and without the Facebook and/or Instagram app installed.

Make sure to use brand new instance of WKWebView with non persistent data store to bring up the login screen, so that it doesn't have any cookies from previous logins:

        let configuration = WKWebViewConfiguration()
        configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
        let webView = WKWebView(frame: .zero, configuration: configuration)

Pray the God of your choosing.


问:
No module named error when packing easyocr with pyinstaller
I found an open-source ready-to-use OCR recognition project called easyocr and used it in my own project. I tried to pack my project with pyinstaller, however, only to get an confusing error:

```<code>File "easyocr\easyocr.py", line 235, in __init__
File "easyocr\recognition.py", line 161, in get_recognizer
File "importlib\__init__.py", line 127, in import_modul
File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked ModuleNotFoundError: No module named 'easyocr.model.model'
</code>```

Then I tried inlcluding the package's path into pathex, and got another error:

```<code>File "PyInstaller\loader\pyimod03_importers.py", line 476, in exec_module
  File "easyocr.py", line 3, in <module>
ImportError: attempted relative import with no known parent package
</code>```

The rough structure of easyocr is like this:

```<code>easyocr/
    |--> __init__.py
    |-->easyocr.py
    |-->recognition.py
    |-->detection.py
    |--> model/
        |--> __init__.py
        |--> model.py
</code>```

Line 161 in recognition.py is as following:

```<code>model_pkg = importlib.import_module("easyocr.model.model")
</code>```

Line 3 in easyocr.py is as following:

```<code>from .detection import get_detector, get_textbox
</code>```

I've got so confused about these importing issues.
Could someone give me some hints to help make it packing my project?

Thanks!

答:
I finally solved this problem by using --collect-all parameter.</p>
```<code>pyinstaller -F project.py --collect-all easyocr
</code>```

Body:
I found an open-source ready-to-use OCR recognition project called easyocr and used it in my own project. I tried to pack my project with pyinstaller, however, only to get an confusing error:

File "easyocr\easyocr.py", line 235, in __init__
File "easyocr\recognition.py", line 161, in get_recognizer
File "importlib\__init__.py", line 127, in import_modul
File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked ModuleNotFoundError: No module named 'easyocr.model.model'

Then I tried inlcluding the package's path into pathex, and got another error:

File "PyInstaller\loader\pyimod03_importers.py", line 476, in exec_module
  File "easyocr.py", line 3, in <module>
ImportError: attempted relative import with no known parent package

The rough structure of easyocr is like this:

easyocr/
    |--> __init__.py
    |-->easyocr.py
    |-->recognition.py
    |-->detection.py
    |--> model/
        |--> __init__.py
        |--> model.py

Line 161 in recognition.py is as following:

model_pkg = importlib.import_module("easyocr.model.model")

Line 3 in easyocr.py is as following:

from .detection import get_detector, get_textbox

I've got so confused about these importing issues.
Could someone give me some hints to help make it packing my project?

Thanks!

Body:
I finally solved this problem by using --collect-all parameter.

pyinstaller -F project.py --collect-all easyocr


问:
What DB to use for a windows application?
I am developing a small windows application in .net. What DB should I use?

DB Structure

Table 1 : Not more than 3 fields : 50+ records (Add, Delete)

Table 2 : Not more than 10 fields : 10-20 records (Add, Delete)

I thought of using XMLDatabase but some say its not advisable to use it. What are the reasons for this?
If it is OK to use XML files as the DB then can you suggest some tutorials or best practices? I have worked with XML files before. Is there anything different when it comes to using it as a DB?

I am also planning to distribute the application to my friends some time later and these guys are not programmers and they are less tech savvy.

So If I am using SQL server how can I deploy it in others machine? How can I add it to the setup file?

Update: Thanks for the answers. I am going for SQLite and here is a tutorial about SQLite and .net if someone is interested.

答:
Table 1 : Not more than 3 fields : 50+
  records (Add, Delete)

Table 2 : Not more than 10 fields :
  10-20 records (Add, Delete)

If your records are of decent size (you don't want to store movies or something like that in your app, would you?) I'd suggest to read from a flat text or xml files at startup and keep everything in memory. You can flush your buffers to disk from time to time.

I don't know your other requirements (what about concurrency?) but imho a real DBMS might be overkill for your problem and sqlite (which I am big fan of) might not be a solution (again concurrency, it's not one of sqlites strengths).

OwnerDisplayName: Shoban

ContentLicense: CC BY-SA 2.5

LastEditorDisplayName: Shoban


问:
Is it possible to have a peer to peer communication using nothing but PHP
Is it possible to implement a p2p using just PHP? Without Flash or Java and obviously without installing some sort of agent/client on one's computer.

so even though it might not be "true" p2p, but it'd use server to establish connection of some sort, but rest of communication must be done using p2p

i apologize for little miscommunication, by "php" i meant not a php binary, but a php script that hosted on web server remote from both peers, so each peer have nothing but a browser.

答:
In case someone comes here seeing if you can write P2P software in PHP, the answer is yes, in this case, Quentin's answer to the original question is correct, PHP would have to be installed on the computer.

You can do whatever you want to do in PHP, including writing true p2p software. To create a true P2P program in PHP, you would use PHP as an interpreted language WITHOUT a web server, and you would use sockets - just like you would in c/c++. The original accepted answer is right and wrong, unless however the original poster was asking if PHP running on a webserver could be a p2p client - which would of course be no.

Basically to do this, you'd basically write a php script that:

Opens a server socket connection (stream_socket_server/socket_create)

Find a list of peer IP's

Open a client connection to each peer

...

Prove everyone wrong.


问:
SendGrid functionality with distribution list email id
I'm looking for sending email using SendGrid whereas my From email address would be a Team Id or Distribution list. But in the Send grid portal I can only authenticate single mail address. Is there any way to achieve this</p>

答:
If you set up domain authentication, you can configure your "From" address to be from any email on the authenticated domain.

https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication

If you need to use single sender verification, that process requires that you (or someone acting on your behalf) have access to the address to complete the verification by clicking on the sent link. In the case of a distribution list email address, this in theory should be able to be done by anybody on the distribution list receiving the email, but everyone will see the verification request.


问:
MySQL doesn't set a default value while adding a new row to the table
I'm writing a web application (that represents online banking work). I have an entity "user" and a corresponding table in the database. Also, I have a registration form that transfers entered data via Thymeleaf.

My entity has two rows that have default values in the database. These are "role" row and "status". I set the default value in the database.

But, when I do input data in the registration form and click "sign up", the data transfers to the database successfully, however, two rows (role and status) that have their own default values are of a null type. Why the default values aren't being set automatically?

My entity class:

```<code>@Data
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Column(name = "email")
    private String email;
    @Column(name = "password")
    private String password;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "status")
    private Status status;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "role")
    private Role role;
}
</code>```

My settings in the database table:

When I check the property "NOT NULL", I'm receiving an error that tells me that the row "role" cannot be null.

That's how my table looks like when I register new users:

My controller:

```<code>@Controller
@RequestMapping("/auth")
public class AuthController {

    private final UserRepository userRepository;

    @Autowired
    public AuthController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/login")
    public String login() {
        return "/auth/login";
    }

    @GetMapping("/signup")
    public String signUp(User user, Model model) {
        return "/auth/registration";
    }

    @PostMapping("/signup")
    public String signUpProcess(@ModelAttribute User user, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "redirect:/auth/registration";
        }

        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        userRepository.save(user);

        return "redirect:/auth/login";
    }

}
</code>```

Registration page:

<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Registration</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/login.css">

    <form class="form-signin" th:method="post" th:object="${user}">

Registration

 Enter all the required information into the forms below.

            <input type="text" id="inputFirstName" th:field="*{firstName}" class="form-control" placeholder="First Name" required>
            First Name

            <input type="text" id="inputLastName" th:field="*{lastName}" class="form-control" placeholder="Last Name" required>
            Last Name

            <input type="email" id="inputEmail" th:field="*{email}" class="form-control" placeholder="Email address" required autofocus>
            Email address

            <input type="password" id="inputPassword" th:field="*{password}" class="form-control" placeholder="Password" required>
            Password

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign up</button>

Already registered? Click to sign in

© BlackBank 2022

    </form>

```
</div>
</div>
</p>
<p>If I haven't specified some valuable information, please, let me know.</p>
<p>Update:</p>
<p>SHOW CREATE TABLE:</p>
```CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `first_name` varchar(100) NOT NULL,
  `last_name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password` varchar(150) NOT NULL,
  `role` varchar(20) NOT NULL DEFAULT 'USER',
  `status` varchar(20) NOT NULL DEFAULT 'ACTIVE',
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_id_uindex` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
```
<p>My Role and Status classes (enums):</p>
<p>Role.class:</p>
```public enum Role {
    // Each role has a specific set of permissions
    USER(Set.of(Permission.USERS_READ)),
    ADMIN(Set.of(Permission.USERS_READ, Permission.USERS_WRITE, Permission.USERS_UPDATE)),
    OWNER(Set.of(Permission.USERS_READ, Permission.USERS_WRITE, Permission.USERS_UPDATE, Permission.USERS_DELETE));

    private Set<Permission> permissions;

    Role(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public Set<SimpleGrantedAuthority> getAuthorities() {
        return getPermissions().stream().map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toSet());
    }

}
```
<p>Status.class:</p>
```public enum Status {
    ACTIVE, BANNED;
}
```

答:
Since the entity getting persisted has null values for those 2 fields it will override the specified defaults.
You can apply 3 approaches

Assign default value in Entity class directly (can be overriden by entity setter)

```<code>private Role role = Roles.user;
</code>```

Use Life cycle call back annotation @Prepersist/@PreUpdate (Most recommended, will override any existing value, can be used to perform logic also.)

As suggested by Amir use attribute "columnDefinition" (can be overriden by entity setter)

Body:
I'm writing a web application (that represents online banking work). I have an entity "user" and a corresponding table in the database. Also, I have a registration form that transfers entered data via Thymeleaf.

My entity has two rows that have default values in the database. These are "role" row and "status". I set the default value in the database.

But, when I do input data in the registration form and click "sign up", the data transfers to the database successfully, however, two rows (role and status) that have their own default values are of a null type. Why the default values aren't being set automatically?

My entity class:

@Data
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Column(name = "email")
    private String email;
    @Column(name = "password")
    private String password;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "status")
    private Status status;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "role")
    private Role role;
}

My settings in the database table:

When I check the property "NOT NULL", I'm receiving an error that tells me that the row "role" cannot be null.

That's how my table looks like when I register new users:

My controller:

@Controller
@RequestMapping("/auth")
public class AuthController {

    private final UserRepository userRepository;

    @Autowired
    public AuthController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/login")
    public String login() {
        return "/auth/login";
    }

    @GetMapping("/signup")
    public String signUp(User user, Model model) {
        return "/auth/registration";
    }

    @PostMapping("/signup")
    public String signUpProcess(@ModelAttribute User user, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "redirect:/auth/registration";
        }

        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        userRepository.save(user);

        return "redirect:/auth/login";
    }

}

Registration page:

<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Registration</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/login.css">

    <form class="form-signin" th:method="post" th:object="${user}">

Registration

 Enter all the required information into the forms below.

            <input type="text" id="inputFirstName" th:field="*{firstName}" class="form-control" placeholder="First Name" required>
            First Name

            <input type="text" id="inputLastName" th:field="*{lastName}" class="form-control" placeholder="Last Name" required>
            Last Name

            <input type="email" id="inputEmail" th:field="*{email}" class="form-control" placeholder="Email address" required autofocus>
            Email address

            <input type="password" id="inputPassword" th:field="*{password}" class="form-control" placeholder="Password" required>
            Password

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign up</button>

Already registered? Click to sign in

© BlackBank 2022

    </form>

If I haven't specified some valuable information, please, let me know.

Update:

SHOW CREATE TABLE:

CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `first_name` varchar(100) NOT NULL,
  `last_name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password` varchar(150) NOT NULL,
  `role` varchar(20) NOT NULL DEFAULT 'USER',
  `status` varchar(20) NOT NULL DEFAULT 'ACTIVE',
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_id_uindex` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

My Role and Status classes (enums):

Role.class:

public enum Role {
    // Each role has a specific set of permissions
    USER(Set.of(Permission.USERS_READ)),
    ADMIN(Set.of(Permission.USERS_READ, Permission.USERS_WRITE, Permission.USERS_UPDATE)),
    OWNER(Set.of(Permission.USERS_READ, Permission.USERS_WRITE, Permission.USERS_UPDATE, Permission.USERS_DELETE));

    private Set<Permission> permissions;

    Role(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public Set<SimpleGrantedAuthority> getAuthorities() {
        return getPermissions().stream().map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toSet());
    }

}

Status.class:

public enum Status {
    ACTIVE, BANNED;
}

Body:
Since the entity getting persisted has null values for those 2 fields it will override the specified defaults.
You can apply 3 approaches

Assign default value in Entity class directly (can be overriden by entity setter)

private Role role = Roles.user;

Use Life cycle call back annotation @Prepersist/@PreUpdate (Most recommended, will override any existing value, can be used to perform logic also.)

As suggested by Amir use attribute "columnDefinition" (can be overriden by entity setter)


问:
JUNIT test for binary search tree
I have implemented a binary search tree. Most of my test in JUNIT test is going through, including these two. I have implementedleavesIsCorrectWhenTreeIsPerfect() and insertValuesInAscendingOrderIncrementsHeight().

Both of these test goes through, however i do not know if it is correctly written based on what their description is asking.

//EDIT: I have added a test that might be a help to one of the test a need help with.

//TODO: help me understand if I have written the correct code for the test in insertValuesInAscendingOrderIncrementsHeight() and leavesIsCorrectWhenTreeIsPerfect() based on what the description for the test is saying.

Have in mind, that I have not included all the tests in the test class, Because the goes through with my implementation of the tree.

Down here I have included my tree class and the test class with both the test I need help with.

```<code>/**
 * An Binary Search tree implementation.
 * @param <T>
 */
public class Tree <T extends Comparable <T>> implements BSTInterface <T>{
    private int size;
    private Node root;
    public class Node{
        private Node Left;
        private Node Right;
        private T data;

        public Node(T data){
            this.data = data;
        }
        public Node getRight(){
            return Right;
        }

        public Node getLeft() {
            return Left;
        }

        public T getData() {
            return data;
        }
    }
    public Tree (){
        size = 0;
        root = null;
    }

    /**
     * Test for presence of a value.
     * @param elem
     * @return true/false
     */
    @Override
    public boolean search(T elem) {
        if(root == null ||elem == null){
        return false;
        }
        Node node = root;
        while(true){
            if(node.data.compareTo(elem) > 0){
                if(node.Right == null){
                    return false;
                } else{
                    node = node.Right;
                }
            } else if(node.data.compareTo(elem) == 0){
                break;
            } else{
                if(node.Left== null){
                    return false;
                }
                else{
                    node = node.Left;
                }
            }
        }
        return true;
    }

    /**
     * Add value to tree; duplicates are not allowed.
     * Return true if the element is not already present (and is thus inserted),
     * false otherwise.
     *
     * @param elem
     * @return true/false
     */
    @Override
    public boolean insert(T elem) {
        if (elem == null){
            return false;
        }
        if (root == null){
            root = new Node(elem);
            size++;
            return true;
        }
        Node node = root;
        while (true){
            if (node.data.compareTo(elem) > 0) {
                if (node.Right == null){
                    node.Right = new Node(elem);
                    size++;
                    break;
                } else {
                    node = node.Right;
                }

            } else if (node.data.compareTo(elem) == 0) {
                return false;
            } else {
                if (node.Left == null){
                    node.Left = new Node(elem);
                    size++;
                    break;
                } else {
                    node = node.Left;
                }
            }
        }
        return true;
    }

    /**
     * the number of elements in the tree
     * @return size.
     */
    @Override
    public int size() {
        return size;
    }

    /**
     * The height of the tree.
     * The empty tree and the tree with only the root node both have height 0.
     * @return the height of the tree.
     */
    @Override
    public int height() {
        return countHeight(root);
    }
    /**
     * Helper method for height
     */
    private int countHeight(Node node){
        if(node == null) {
            return 0;
        }
        if (node.Left == null && node.Right == null) {
            return 0;
        }
        return 1 + Math.max(countHeight(node.getLeft()), countHeight(node.getRight()));
    }

    /**
     * The number of leaves in the tree.
     * @return the amount of leaves the tree has.
     */
    @Override
    public int leaves() {
        return countLeaves(root);
    }
    /**
     * Helper method for leaves
     */
    private int countLeaves(Node node) {
        if (node == null) {
            return 0;
        }
        if (node.Left == null && node.Right == null) {
            return 1;
        }
        return countLeaves(node.Left) + countLeaves(node.Right);
    }

    /**
     * A string describing the tree
     * @return
     */
    public String toString(){
        String str = "[" + helpToString(root);
        if (str.length() > 1) {
            str = str.substring(0, str.length() - 2);
        } return str + "]";
    }

    /**
     * Helper method for toString
     */
    private String helpToString(Node node) {
        String str = "";
        if (node != null) {
            str += helpToString(node.Right);
            str += node.data + ", ";
            str += helpToString(node.Left);
        }
        return str;
      }
}

</code>```
```<code>import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.Timeout;
import static org.junit.Assert.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
 * Test class for a tree.
 */
public class TreeTest{
    @Rule public Timeout globalTimeout = Timeout.seconds(5);

    Tree<Integer> tree;
    int[] elementsInTree;
    int[] elementsNotInTree;

    @Before
    public void setUp() {
        /**
         * This tree should look like this:
         *
         *               8
         *             /  \
         *            3   10
         *           / \    \
         *          1   6    14
         *             / \   /
         *            4   7 13
         */
        tree = new Tree<>();
        elementsInTree = new int[] {8, 10, 14, 13, 3, 1, 6, 4, 7};
        for (int elem : elementsInTree) {
            tree.insert(elem);
        }
        elementsNotInTree = new int[] {34, -3, -10, 12, 74, 5};
    }
 @Test
    public void heightIsLogOfNumLeavesTreeIsPerfect() {
        // For a perfect tree, tree.height() == log2(tree.leaves()

        // Arrange
        Tree<Integer> tree = new Tree<>();
        int[] elements = new int[] {8, 3, 10, 1, 6, 9, 14};
        int numLeaves = 4;
        int logNumLeaves = (int) Math.round(Math.log(numLeaves) / Math.log(2));
        for (int elem : elements) {
            tree.insert(elem);
        }

        // Act
        int height = tree.height();
        // Assert
        assertThat(height, equalTo(logNumLeaves));
    }

    @Test
    public void leavesIsCorrectWhenTreeIsPerfect() { //TEST
        // A perfect tree has all leaves at the same depth, and all internal nodes
        // (i.e. non-leaves) have two children
        //
        // This test should assert that a perfect tree with 2*n-1 nodes total,
        // has exactly n leaves (i.e. that Tree.leaves() returns n).
        //
        // An example is the perfect three-node tree from the test above:
        //
        //                        (1338)
        //                        /    \
        //                    (1337)  (1396)

        // You have to construct your own tree here, with n >= 4

    Tree <Integer> tree = new Tree<>();
       int n = 4;
       for(int i = 0; i>=n; i++) {
           tree.insert(i);
         int numLeaves = 2*n-1;
         int leaves = tree.leaves();
         assertThat(leaves,equalTo(numLeaves));
       }
    }

    // Tests for insert/height
    @Test
    public void insertValuesInAscendingOrderIncrementsHeight() { //TEST
        // When inserting elements in ascending order, each element is inserted
        // to the right of the deepest node, so the height should increment by
        // 1 for each element inserted.
        Tree <Integer> tree = new Tree<>();
        int val = 100;
        for(int i = 0; i < val; i++){
            tree.insert(i);

        }
            int treeHeight = tree.height();
        treeHeight++;
            assertThat(tree.size(),equalTo(treeHeight));
    }
}

</code>```

答:
```<code>for(int i = 0; i>=n; i++) {
tree.insert(i);
</code>```

The condition of your for loop is always false.

Title: JUNIT test for binary search tree

Body:
I have implemented a binary search tree. Most of my test in JUNIT test is going through, including these two. I have implementedleavesIsCorrectWhenTreeIsPerfect() and insertValuesInAscendingOrderIncrementsHeight().

Both of these test goes through, however i do not know if it is correctly written based on what their description is asking.

//EDIT: I have added a test that might be a help to one of the test a need help with.

//TODO: help me understand if I have written the correct code for the test in insertValuesInAscendingOrderIncrementsHeight() and leavesIsCorrectWhenTreeIsPerfect() based on what the description for the test is saying.

Have in mind, that I have not included all the tests in the test class, Because the goes through with my implementation of the tree.

Down here I have included my tree class and the test class with both the test I need help with.

/**
 * An Binary Search tree implementation.
 * @param <T>
 */
public class Tree <T extends Comparable <T>> implements BSTInterface <T>{
    private int size;
    private Node root;
    public class Node{
        private Node Left;
        private Node Right;
        private T data;

        public Node(T data){
            this.data = data;
        }
        public Node getRight(){
            return Right;
        }

        public Node getLeft() {
            return Left;
        }

        public T getData() {
            return data;
        }
    }
    public Tree (){
        size = 0;
        root = null;
    }

    /**
     * Test for presence of a value.
     * @param elem
     * @return true/false
     */
    @Override
    public boolean search(T elem) {
        if(root == null ||elem == null){
        return false;
        }
        Node node = root;
        while(true){
            if(node.data.compareTo(elem) > 0){
                if(node.Right == null){
                    return false;
                } else{
                    node = node.Right;
                }
            } else if(node.data.compareTo(elem) == 0){
                break;
            } else{
                if(node.Left== null){
                    return false;
                }
                else{
                    node = node.Left;
                }
            }
        }
        return true;
    }

    /**
     * Add value to tree; duplicates are not allowed.
     * Return true if the element is not already present (and is thus inserted),
     * false otherwise.
     *
     * @param elem
     * @return true/false
     */
    @Override
    public boolean insert(T elem) {
        if (elem == null){
            return false;
        }
        if (root == null){
            root = new Node(elem);
            size++;
            return true;
        }
        Node node = root;
        while (true){
            if (node.data.compareTo(elem) > 0) {
                if (node.Right == null){
                    node.Right = new Node(elem);
                    size++;
                    break;
                } else {
                    node = node.Right;
                }

            } else if (node.data.compareTo(elem) == 0) {
                return false;
            } else {
                if (node.Left == null){
                    node.Left = new Node(elem);
                    size++;
                    break;
                } else {
                    node = node.Left;
                }
            }
        }
        return true;
    }

    /**
     * the number of elements in the tree
     * @return size.
     */
    @Override
    public int size() {
        return size;
    }

    /**
     * The height of the tree.
     * The empty tree and the tree with only the root node both have height 0.
     * @return the height of the tree.
     */
    @Override
    public int height() {
        return countHeight(root);
    }
    /**
     * Helper method for height
     */
    private int countHeight(Node node){
        if(node == null) {
            return 0;
        }
        if (node.Left == null && node.Right == null) {
            return 0;
        }
        return 1 + Math.max(countHeight(node.getLeft()), countHeight(node.getRight()));
    }

    /**
     * The number of leaves in the tree.
     * @return the amount of leaves the tree has.
     */
    @Override
    public int leaves() {
        return countLeaves(root);
    }
    /**
     * Helper method for leaves
     */
    private int countLeaves(Node node) {
        if (node == null) {
            return 0;
        }
        if (node.Left == null && node.Right == null) {
            return 1;
        }
        return countLeaves(node.Left) + countLeaves(node.Right);
    }

    /**
     * A string describing the tree
     * @return
     */
    public String toString(){
        String str = "[" + helpToString(root);
        if (str.length() > 1) {
            str = str.substring(0, str.length() - 2);
        } return str + "]";
    }

    /**
     * Helper method for toString
     */
    private String helpToString(Node node) {
        String str = "";
        if (node != null) {
            str += helpToString(node.Right);
            str += node.data + ", ";
            str += helpToString(node.Left);
        }
        return str;
      }
}

import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.Timeout;
import static org.junit.Assert.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
 * Test class for a tree.
 */
public class TreeTest{
    @Rule public Timeout globalTimeout = Timeout.seconds(5);

    Tree<Integer> tree;
    int[] elementsInTree;
    int[] elementsNotInTree;

    @Before
    public void setUp() {
        /**
         * This tree should look like this:
         *
         *               8
         *             /  \
         *            3   10
         *           / \    \
         *          1   6    14
         *             / \   /
         *            4   7 13
         */
        tree = new Tree<>();
        elementsInTree = new int[] {8, 10, 14, 13, 3, 1, 6, 4, 7};
        for (int elem : elementsInTree) {
            tree.insert(elem);
        }
        elementsNotInTree = new int[] {34, -3, -10, 12, 74, 5};
    }
 @Test
    public void heightIsLogOfNumLeavesTreeIsPerfect() {
        // For a perfect tree, tree.height() == log2(tree.leaves()

        // Arrange
        Tree<Integer> tree = new Tree<>();
        int[] elements = new int[] {8, 3, 10, 1, 6, 9, 14};
        int numLeaves = 4;
        int logNumLeaves = (int) Math.round(Math.log(numLeaves) / Math.log(2));
        for (int elem : elements) {
            tree.insert(elem);
        }

        // Act
        int height = tree.height();
        // Assert
        assertThat(height, equalTo(logNumLeaves));
    }

    @Test
    public void leavesIsCorrectWhenTreeIsPerfect() { //TEST
        // A perfect tree has all leaves at the same depth, and all internal nodes
        // (i.e. non-leaves) have two children
        //
        // This test should assert that a perfect tree with 2*n-1 nodes total,
        // has exactly n leaves (i.e. that Tree.leaves() returns n).
        //
        // An example is the perfect three-node tree from the test above:
        //
        //                        (1338)
        //                        /    \
        //                    (1337)  (1396)

        // You have to construct your own tree here, with n >= 4

    Tree <Integer> tree = new Tree<>();
       int n = 4;
       for(int i = 0; i>=n; i++) {
           tree.insert(i);
         int numLeaves = 2*n-1;
         int leaves = tree.leaves();
         assertThat(leaves,equalTo(numLeaves));
       }
    }

    // Tests for insert/height
    @Test
    public void insertValuesInAscendingOrderIncrementsHeight() { //TEST
        // When inserting elements in ascending order, each element is inserted
        // to the right of the deepest node, so the height should increment by
        // 1 for each element inserted.
        Tree <Integer> tree = new Tree<>();
        int val = 100;
        for(int i = 0; i < val; i++){
            tree.insert(i);

        }
            int treeHeight = tree.height();
        treeHeight++;
            assertThat(tree.size(),equalTo(treeHeight));
    }
}

Body:
for(int i = 0; i>=n; i++) {
tree.insert(i);

The condition of your for loop is always false.


问:
SESSION_COOKIE_SECURE=True in Django 2.2 redirects Admin login to itself
I have a simple django 2.2 project that I trying to publish. I am trying to secure the website as best I can. One of the options is SESSION_COOKIE_SECURE. When set to True, my admin pages just won't work. When I go to /admin page, I will see a login. Inputting the correct login and password just brings me back to the exact same page. If I input the wrong login and password, then an error message pops up. So I know I have used the right password. There's nothing special about my admin setup. It's just in the urls.py of the project folder

```<code>urlpatterns = [
    path('admin/', admin.site.urls), ...
</code>```

Of course if I set SESSION_COOKIE_SECURE to False, the admin pages works. I have tried setting the SESSION_COOKIE_DOMAIN to my domain and that doesn't help. What am I missing? Thanks.

EDIT: I have just tried the following set of options based on the Django docs but I am not getting it working.

```<code>SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
</code>```

ANOTHER EDIT: So the above works in Production (which has HTTPS) but not in Development. This is good but also troubling to some degree. We want our production and development environment to be as close as possible so that features and stuff can be tested. I wonder what the best practice is when it comes to testing these troublesome little Django settings.

答:
It seems the SESSION_COOKIE_SECURE option works correctly only under the HTTPS connection.

But, I have no idea why does it work under HTTP in my local environment with default Django's manage.py runserver but not Nginx / gunicorn in production...

Body:
I have a simple django 2.2 project that I trying to publish. I am trying to secure the website as best I can. One of the options is SESSION_COOKIE_SECURE. When set to True, my admin pages just won't work. When I go to /admin page, I will see a login. Inputting the correct login and password just brings me back to the exact same page. If I input the wrong login and password, then an error message pops up. So I know I have used the right password. There's nothing special about my admin setup. It's just in the urls.py of the project folder

urlpatterns = [
    path('admin/', admin.site.urls), ...

Of course if I set SESSION_COOKIE_SECURE to False, the admin pages works. I have tried setting the SESSION_COOKIE_DOMAIN to my domain and that doesn't help. What am I missing? Thanks.

EDIT: I have just tried the following set of options based on the Django docs but I am not getting it working.

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

ANOTHER EDIT: So the above works in Production (which has HTTPS) but not in Development. This is good but also troubling to some degree. We want our production and development environment to be as close as possible so that features and stuff can be tested. I wonder what the best practice is when it comes to testing these troublesome little Django settings.


问:
Abstract transactions with service/repository/unitofwork
I have a ton of transactional db access code intermixed with business logic in legacy code.
I want to separate it to services and repositories, with transaction control outside services and abstract away transactions for testability.

The problem is, i need to have IDbConnection parameter, enlisted in transaction, for every method signature both in service and repository layers, and that is what i certainly don't like:

```<code>void Process(Stuff s, IDbConnection connection);
</code>```

I want to figure out a way to have something like this in a top level method:

```<code>void Process(Stuff stuff, User u)
{
 using(var UoW = ???) // Start transaction
 {
  // various operations in the same transaction
  if( AuthorizationService.AuthorizesUserForStuff(u,stuff) )
  {
   StuffService.Process(stuff);
   AuditService.Success(u, stuff);
  }
  else
   AuditService.Failure(u,stuff);
  UoW.Commit();
 }
}
</code>```

Repository:

```<code>void Process(Stuff s)
{
 IDbCommand command = ??? //aware of current UnitOfWork
 ...
 command.ExecuteNonQuery();
}
</code>```

The legacy code does not use any kind of ORM, it's a lot of stored procedure and custom sql code, also some operations require roundtrips to database, so i can't just register the objects with unitofwork, and carry out operations in commit method.

Please suggest what pattern can be used in this situation for some persistence ignorance and will allow to control transactions at the top level.

答:
We use OR-mapping for this, so our solution for this looks different. But one way to do accomplish the same thing could be to use an abstracted persistance context which deals with concrete database stuff, inject this into all the service classes that use it and create your unit of work from this context at the 'process' level, and then allow the UoW instance to manage transaction state and connections by collaborating with the context instance.

Something like

```<code>        using (var unitOfWork = Context.CreateUnitOfWork())
        {
            // access injected services and repositories to modify stuff

            unitOfWork.Complete();
        }
</code>```

When actually doing DB-stuff, in your situation you would aquire a DB connection from your (injected) context instance, something like this

```<code>        using (var conn = Context.AquireDbConnection())
        using (var command = conn.CreateDbCommand())
        {
            // do db stuff

            command.ExecuteNonQuery();
        }
</code>```

your concrete unit of work/context implementation should be smart enough here to recognize if the connection returned should be a new or ambient connection, based on if there is an active ambient business transaction there or not.

Obviously, you would need per-request isolation for this mechanism and life time of context instance would be per request.

ContentLicense: CC BY-SA 3.0

Body:
I have a ton of transactional db access code intermixed with business logic in legacy code.
I want to separate it to services and repositories, with transaction control outside services and abstract away transactions for testability.

The problem is, i need to have IDbConnection parameter, enlisted in transaction, for every method signature both in service and repository layers, and that is what i certainly don't like:

void Process(Stuff s, IDbConnection connection);

I want to figure out a way to have something like this in a top level method:

void Process(Stuff stuff, User u)
{
 using(var UoW = ???) // Start transaction
 {
  // various operations in the same transaction
  if( AuthorizationService.AuthorizesUserForStuff(u,stuff) )
  {
   StuffService.Process(stuff);
   AuditService.Success(u, stuff);
  }
  else
   AuditService.Failure(u,stuff);
  UoW.Commit();
 }
}

Repository:

void Process(Stuff s)
{
 IDbCommand command = ??? //aware of current UnitOfWork
 ...
 command.ExecuteNonQuery();
}

The legacy code does not use any kind of ORM, it's a lot of stored procedure and custom sql code, also some operations require roundtrips to database, so i can't just register the objects with unitofwork, and carry out operations in commit method.

Please suggest what pattern can be used in this situation for some persistence ignorance and will allow to control transactions at the top level.

Body:
We use OR-mapping for this, so our solution for this looks different. But one way to do accomplish the same thing could be to use an abstracted persistance context which deals with concrete database stuff, inject this into all the service classes that use it and create your unit of work from this context at the 'process' level, and then allow the UoW instance to manage transaction state and connections by collaborating with the context instance.

Something like

        using (var unitOfWork = Context.CreateUnitOfWork())
        {
            // access injected services and repositories to modify stuff

            unitOfWork.Complete();
        }

When actually doing DB-stuff, in your situation you would aquire a DB connection from your (injected) context instance, something like this

        using (var conn = Context.AquireDbConnection())
        using (var command = conn.CreateDbCommand())
        {
            // do db stuff

            command.ExecuteNonQuery();
        }

your concrete unit of work/context implementation should be smart enough here to recognize if the connection returned should be a new or ambient connection, based on if there is an active ambient business transaction there or not.

Obviously, you would need per-request isolation for this mechanism and life time of context instance would be per request.


问:
SyntaxError: Cannot use import statement outside a module when setting up socket.io in adonisjs
am following socketio with adonisjs to be able to implement web sockets but I get
this error

```<code>[ info ]  building project...
[ info ]  starting http server...

SyntaxError: Cannot use import statement outside a module

at /home/../providers/AppProvider.ts(anonymous):18
13    }
14
15    public async ready () {
16      // App is ready
17      if (this.app.environment === 'web') {
18        await import('../start/socket')
19
20      }
21    }
22
23    public async shutdown () {

[ encore ] Running webpack-dev-server ...
[ warn ]  Underlying HTTP server died with "0 code"

</code>```

when I try out this solution of adding "type": "module", to package.json I get other errors

how can I fix this?

答:
```<code>{
  "name": "socket",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "nodemon": "^2.0.15",
    "socket.io": "^4.4.0"
  }
}
</code>```

Check your package.json file,  "type": "module" is missing , Make sure to add this as shown in this one !!!

Body:
am following socketio with adonisjs to be able to implement web sockets but I get
this error

[ info ]  building project...
[ info ]  starting http server...

SyntaxError: Cannot use import statement outside a module

at /home/../providers/AppProvider.ts(anonymous):18
13    }
14
15    public async ready () {
16      // App is ready
17      if (this.app.environment === 'web') {
18        await import('../start/socket')
19
20      }
21    }
22
23    public async shutdown () {

[ encore ] Running webpack-dev-server ...
[ warn ]  Underlying HTTP server died with "0 code"

when I try out this solution of adding "type": "module", to package.json I get other errors

how can I fix this?

Body:
{
  "name": "socket",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "nodemon": "^2.0.15",
    "socket.io": "^4.4.0"
  }
}

Check your package.json file,  "type": "module" is missing , Make sure to add this as shown in this one !!!


问:
How can i get data from an array of object with matching key and combine the other results into array
i want the result below

Given an array

```<code>[{
      "date": "JAN",
      "value": 5,
      "weight": 3
  }, {
      "date": "JAN",
      "value": 4,
      "weight": 23
  }, {
      "date": "FEB",
      "value": 9,
      "weight": 1
  }, {
      "date": "FEB",
      "value": 10,
      "weight": 30
  }]
</code>```

and a key 'date'

transform it into following output:

```<code> [{
       "date": "JAN",
       "value": [5, 4],
       "weight": [3, 23]
   }, {
       "date": "FEB",
       "value": [9, 10],
       "weight": [1, 30]
   }]
</code>```

any help would be much appreciated thank You in advance

答:
Using Array#reduce, iterate over the array while updating a Map where the primaryKey is the key and the grouped object is the value

In every iteration:

get the primary-key value and the remaining properties from the current object

Using Object#entries, get the list of pairs from the properties

Using Map#get, get the current primary-key value if exists

If it does, we need to iterate over the entries using Array#forEach and update the arrays

If not, we create the initial object and add a new pair using Map#set

Finally, using Map#values, you can get the list of grouped objects

const transform = (arr, primaryKey) =>
  [...
    arr.reduce((map, e) => {

      const { [primaryKey]: key, ...props } = e;
      const currentProps = Object.entries(props);
      const item = map.get(key);

      if(item) {
        currentProps.forEach(([ k, v ]) => item[k] = [...(item[k] ?? []), v]);
      } else {
        const obj = currentProps.reduce((acc, [ k, v ]) => ({
          ...acc, [k]: Array.isArray(v) ? [...v] : [v]
        }), { primaryKey: key });
        map.set(key, obj);
      }

      return map;
    }, new Map)
    .values()
  ];

console.log( transform([ { "date": "JAN", "value": 5, "weight": 3 }, { "date": "JAN", "value": 4, "weight": 23 }, { "date": "FEB", "value": 9, "weight": 1 }, { "date": "FEB", "value": 10, "weight": 30 } ], 'date') );
console.log( transform([ { type: '200', api: [ '/counter' ] }, { type: '400', api: [ '/counter' ] }, { type: '500', api: [ '/counter', '/product' ] } ], 'type') );```

Body:
i want the result below

Given an array

[{
      "date": "JAN",
      "value": 5,
      "weight": 3
  }, {
      "date": "JAN",
      "value": 4,
      "weight": 23
  }, {
      "date": "FEB",
      "value": 9,
      "weight": 1
  }, {
      "date": "FEB",
      "value": 10,
      "weight": 30
  }]

and a key 'date'

transform it into following output:

 [{
       "date": "JAN",
       "value": [5, 4],
       "weight": [3, 23]
   }, {
       "date": "FEB",
       "value": [9, 10],
       "weight": [1, 30]
   }]

any help would be much appreciated thank You in advance